# 2.11BSD#431 - /usr/src/usr.sbin/named/named/ns_forw.c

##### Functions

    ns_forw           X
    nslookup          X
    qcomp             X
    qfindid           X
    qflush            X
    qfree             X
    qnew              X
    qremove           X
    retry             X
    retrytime         X
    schedretry        X
    unsched           X

##### Variables

    fwdtab            X
    nsid              X
    qhead             X
    retryqp           X

       1: /*
       2:  * Copyright (c) 1986 Regents of the University of California.
       3:  * All rights reserved.
       4:  *
       5:  * Redistribution and use in source and binary forms are permitted
       6:  * provided that this notice is preserved and that due credit is given
       7:  * to the University of California at Berkeley. The name of the University
       8:  * may not be used to endorse or promote products derived from this
       9:  * software without specific prior written permission. This software
      10:  * is provided ``as is'' without express or implied warranty.
      11:  */
      12:
      13: #ifndef lint
      14: static char sccsid[] = "@(#)ns_forw.c	4.26 (Berkeley) 3/28/88";
      15: #endif /* not lint */
      16:
      17: #include <stdio.h>
      18: #include <sys/param.h>
      19: #include <sys/time.h>
      20: #include <sys/socket.h>
      21: #include <netinet/in.h>
      22: #include <syslog.h>
      23: #include <arpa/nameser.h>
      24: #include "ns.h"
      25: #include "db.h"
      26:
      27: struct  qinfo *qhead = QINFO_NULL;  /* head of allocated queries */
      28: struct  qinfo *retryqp = QINFO_NULL;    /* list of queries to retry */
      29: struct  fwdinfo *fwdtab;        /* list of forwarding hosts */
      30:
      31: int nsid;               /* next forwarded query id */
      32: extern int forward_only;        /* you are only a slave */
      33: extern int errno;
      34: extern short ns_port;
      35:
      36: time_t  retrytime();
      37:
      38: /*
      39:  * Forward the query to get the answer since its not in the database.
      40:  * Returns FW_OK if a request struct is allocated and the query sent.
      41:  * Returns FW_DUP if this is a duplicate of a pending request.
      42:  * Returns FW_NOSERVER if there were no addresses for the nameservers.
      43:  * Returns FW_SERVFAIL on malloc error.
      44:  * (no action is taken on errors and qpp is not filled in.)
      45:  */
      46: ns_forw(nsp, msg, msglen, fp, qsp, dfd, qpp)
      47:     struct databuf *nsp[];
      48:     char *msg;
      49:     int msglen;
      50:     struct sockaddr_in *fp;
      51:     struct qstream *qsp;
      52:     int dfd;
      53:     struct qinfo **qpp;
      54: {
      55:     register struct qinfo *qp;
      56:     HEADER *hp;
      57:     u_short id;
      58:     extern char *calloc();
      59:
      60: #ifdef DEBUG
      61:     if (debug >= 3)
      62:         fprintf(ddt,"ns_forw()\n");
      63: #endif
      64:
      65:     /* Don't forward if we're already working on it. */
      66:     hp = (HEADER *) msg;
      67:     id = hp->id;
      68:     hp->rd = 0;
      69:     /* Look at them all */
      70:     for (qp = qhead; qp!=QINFO_NULL; qp = qp->q_link) {
      71:         if (qp->q_id == id &&
      72:             bcmp((char *)&qp->q_from, fp, sizeof(qp->q_from)) == 0 &&
      73:             (qp->q_cmsglen == 0 && qp->q_msglen == msglen &&
      74:              bcmp((char *)qp->q_msg+2, msg+2, msglen-2) == 0) ||
      75:             (qp->q_cmsglen == msglen &&
      76:              bcmp((char *)qp->q_cmsg+2, msg+2, msglen-2) == 0)) {
      77: #ifdef DEBUG
      78:             if (debug >= 3)
      79:                 fprintf(ddt,"forw: dropped DUP id=%d\n", ntohs(id));
      80: #endif
      81: #ifdef STATS
      82:             stats[S_DUPQUERIES].cnt++;
      83: #endif
      84:             return (FW_DUP);
      85:         }
      86:     }
      87:
      88:     qp = qnew();
      89:     if (nslookup(nsp, qp) == 0) {
      90: #ifdef DEBUG
      91:         if (debug >= 2)
      92:             fprintf(ddt,"forw: no nameservers found\n");
      93: #endif
      94:         qfree(qp);
      95:         hp->rd = 1;
      96:         return (FW_NOSERVER);
      97:     }
      98:     qp->q_stream = qsp;
      99:     qp->q_curaddr = 0;
     100:     qp->q_fwd = fwdtab;
     101:     qp->q_dfd = dfd;
     102:     qp->q_id = id;
     103:     hp->id = qp->q_nsid = htons((u_short)++nsid);
     104:     hp->ancount = 0;
     105:     hp->nscount = 0;
     106:     hp->arcount = 0;
     107:     if (qp->q_fwd)
     108:         hp->rd = 1;
     109:     else
     110:         qp->q_addr[0].stime = tt;
     111:     qp->q_from = *fp;
     112:     if ((qp->q_msg = malloc((unsigned)msglen)) == NULL) {
     113:         syslog(LOG_ERR, "forw: %m");
     114:         qfree(qp);
     115:         return (FW_SERVFAIL);
     116:     }
     117:     bcopy(msg, qp->q_msg, qp->q_msglen = msglen);
     118:
     119:     schedretry(qp, retrytime(qp));
     120: #ifdef DEBUG
     121:     if (debug)
     122:         fprintf(ddt,
     123:            "forw: forw -> %s %d (%d) nsid=%d id=%d %ldms retry %ld sec\n",
     124:             inet_ntoa(Q_NEXTADDR(qp,0)->sin_addr),
     125:             ds, ntohs(Q_NEXTADDR(qp,0)->sin_port),
     126:             ntohs(qp->q_nsid), ntohs(qp->q_id),
     127:             qp->q_addr[0].nsdata->d_nstime,
     128:             qp->q_time - tt.tv_sec);
     129:     if ( debug >= 10)
     130:         fp_query(msg, ddt);
     131: #endif
     132:     if (sendto(ds, msg, msglen, 0, (struct sockaddr *)Q_NEXTADDR(qp,0),
     133:            sizeof(struct sockaddr_in)) < 0){
     134: #ifdef DEBUG
     135:         if (debug >= 5)
     136:             fprintf(ddt,"error returning msg errno=%d\n",errno);
     137: #endif
     138:     }
     139: #ifdef STATS
     140:     stats[S_OUTPKTS].cnt++;
     141: #endif
     142:     if (qpp)
     143:         *qpp = qp;
     144:     hp->rd = 0;
     145:     return (0);
     146: }
     147:
     148: /*
     149:  * Lookup the address for each nameserver in `nsp' and add it to
     150:  * the list saved in the qinfo structure.
     151:  */
     152: nslookup(nsp, qp)
     153:     struct databuf *nsp[];
     154:     register struct qinfo *qp;
     155: {
     156:     register struct namebuf *np;
     157:     register struct databuf *dp, *nsdp;
     158:     register struct qserv *qs;
     159:     register int n, i;
     160:     struct hashbuf *tmphtp;
     161:     char *dname, *fname;
     162:     int oldn, naddr, class, found_arr;
     163:     time_t curtime;
     164:     int qcomp();
     165:
     166: #ifdef DEBUG
     167:     if (debug >= 3)
     168:         fprintf(ddt,"nslookup(nsp=x%x,qp=x%x)\n",nsp,qp);
     169: #endif
     170:
     171:     naddr = n = qp->q_naddr;
     172:     curtime = (u_long) tt.tv_sec;
     173:     while ((nsdp = *nsp++) != NULL) {
     174:         class = nsdp->d_class;
     175:         dname = nsdp->d_data;
     176: #ifdef DEBUG
     177:         if (debug >= 3)
     178:             fprintf(ddt,"nslookup: NS %s c%d t%d (x%x)\n",
     179:                 dname, class, nsdp->d_type, nsdp->d_flags);
     180: #endif
     181:         /* don't put in people we have tried */
     182:         for (i = 0; i < qp->q_nusedns; i++)
     183:             if (qp->q_usedns[i] == nsdp) {
     184: #ifdef DEBUG
     185:                 if (debug >= 2)
     186: fprintf(ddt, "skipping used NS w/name %s\n", nsdp->d_data);
     187: #endif DEBUG
     188:                 goto skipserver;
     189:             }
     190:
     191:         tmphtp = ((nsdp->d_flags & DB_F_HINT) ? fcachetab : hashtab);
     192:         np = nlookup(dname, &tmphtp, &fname, 1);
     193:         if (np == NULL || fname != dname) {
     194: #ifdef DEBUG
     195:             if (debug >= 3)
     196:                 fprintf(ddt,"%s: not found %s %x\n",dname,fname,np);
     197: #endif
     198:             continue;
     199:         }
     200:         found_arr = 0;
     201:         oldn = n;
     202:         /* look for name server addresses */
     203:         for (dp = np->n_data; dp != NULL; dp = dp->d_next) {
     204:             if (dp->d_type != T_A || dp->d_class != class)
     205:                 continue;
     206:             /*
     207: 			 * Don't use records that may become invalid to
     208: 			 * reference later when we do the rtt computation.
     209: 			 * Never delete our safety-belt information!
     210: 			 */
     211:             if ((dp->d_zone == 0) &&
     212:                 (dp->d_ttl < (curtime+900)) &&
     213:                 !(dp->d_flags & DB_F_HINT) )
     214:                 {
     215: #ifdef DEBUG
     216:                 if (debug >= 3)
     217:                     fprintf(ddt,"nslookup: stale entry '%s'\n",
     218:                         np->n_dname);
     219: #endif
     220:                 /* Cache invalidate the NS RR's */
     221:                 if (dp->d_ttl < curtime)
     222:                     delete_all(np, class, T_A);
     223:                 n = oldn;
     224:                 break;
     225:             }
     226:
     227:             found_arr++;
     228:             /* don't put in duplicates */
     229:             qs = qp->q_addr;
     230:             for (i = 0; i < n; i++, qs++)
     231:                 if (bcmp((char *)&qs->ns_addr.sin_addr,
     232:                     dp->d_data, sizeof(struct in_addr)) == 0)
     233:                     goto skipaddr;
     234:             qs->ns_addr.sin_family = AF_INET;
     235:             qs->ns_addr.sin_port = (u_short)ns_port;
     236:             qs->ns_addr.sin_addr =
     237:                     *(struct in_addr *)dp->d_data;
     238:             qs->ns = nsdp;
     239:             qs->nsdata = dp;
     240:             qp->q_addr[n].nretry = 0;
     241:             n++;
     242:             if (n >= NSMAX)
     243:                 goto out;
     244:     skipaddr:   ;
     245:         }
     246: #ifdef DEBUG
     247:         if (debug >= 3)
     248:             fprintf(ddt,"nslookup: %d ns addrs\n", n);
     249: #endif
     250:         if (found_arr == 0 && qp->q_system == 0)
     251:             (void) sysquery(dname, class, T_A);
     252: skipserver: ;
     253:     }
     254: out:
     255: #ifdef DEBUG
     256:     if (debug >= 3)
     257:         fprintf(ddt,"nslookup: %d ns addrs total\n", n);
     258: #endif
     259:     qp->q_naddr = n;
     260:     if (n > 1)
     261:         qsort((char *)qp->q_addr, n, sizeof(struct qserv), qcomp);
     262:     return (n - naddr);
     263: }
     264:
     265: qcomp(qs1, qs2)
     266:     struct qserv *qs1, *qs2;
     267: {
     268:     register u_long qservcmp =
     269:         qs1->nsdata->d_nstime - qs2->nsdata->d_nstime;
     270:
     271:     return (qservcmp > 0 ? 1 : (qservcmp == 0 ? 0 : -1));
     272: }
     273:
     274: /*
     275:  * Arrange that forwarded query (qp) is retried after t seconds.
     276:  */
     277: schedretry(qp, t)
     278:     struct qinfo *qp;
     279:     time_t t;
     280: {
     281:     register struct qinfo *qp1, *qp2;
     282:
     283: #ifdef DEBUG
     284:     if (debug > 3) {
     285:         fprintf(ddt,"schedretry(%x, %ldsec)\n", qp, t);
     286:         if (qp->q_time)
     287:            fprintf(ddt,"WARNING: schedretry(%x,%ld) q_time already %ld\n", qp, t, qp->q_time);
     288:     }
     289: #endif
     290:     t += (u_long) tt.tv_sec;
     291:     qp->q_time = t;
     292:
     293:     if ((qp1 = retryqp) == NULL) {
     294:         retryqp = qp;
     295:         qp->q_next = NULL;
     296:         return;
     297:     }
     298:     while ((qp2 = qp1->q_next) != NULL && qp2->q_time < t)
     299:         qp1 = qp2;
     300:     qp1->q_next = qp;
     301:     qp->q_next = qp2;
     302: }
     303:
     304: /*
     305:  * Unsched is called to remove a forwarded query entry.
     306:  */
     307: unsched(qp)
     308:     struct qinfo *qp;
     309: {
     310:     register struct qinfo *np;
     311:
     312: #ifdef DEBUG
     313:     if (debug > 3) {
     314:         fprintf(ddt,"unsched(%x, %d )\n", qp, ntohs(qp->q_id));
     315:     }
     316: #endif
     317:     if( retryqp == qp )  {
     318:         retryqp = qp->q_next;
     319:     } else {
     320:         for( np=retryqp; np->q_next != QINFO_NULL; np = np->q_next ) {
     321:             if( np->q_next != qp)
     322:                 continue;
     323:             np->q_next = qp->q_next;    /* dequeue */
     324:             break;
     325:         }
     326:     }
     327:     qp->q_next = QINFO_NULL;        /* sanity check */
     328:     qp->q_time = 0;
     329: }
     330:
     331: /*
     332:  * Retry is called to retransmit query 'qp'.
     333:  */
     334: retry(qp)
     335:     register struct qinfo *qp;
     336: {
     337:     register int n;
     338:     register HEADER *hp;
     339:
     340: #ifdef DEBUG
     341:     if (debug > 3)
     342:         fprintf(ddt,"retry(x%x) id=%d\n", qp, ntohs(qp->q_id));
     343: #endif
     344:     if((HEADER *)qp->q_msg == NULL) {       /*** XXX ***/
     345:         qremove(qp);
     346:         return;
     347:     }                       /*** XXX ***/
     348:
     349:     /* try next address */
     350:     n = qp->q_curaddr;
     351:     if (qp->q_fwd) {
     352:         qp->q_fwd = qp->q_fwd->next;
     353:         if (qp->q_fwd)
     354:             goto found;
     355:         /* out of forwarders, try direct queries */
     356:     } else
     357:         ++qp->q_addr[n].nretry;
     358:     if (!forward_only) {
     359:         do {
     360:             if (++n >= qp->q_naddr)
     361:                 n = 0;
     362:             if (qp->q_addr[n].nretry < MAXRETRY)
     363:                 goto found;
     364:         } while (n != qp->q_curaddr);
     365:     }
     366:     /*
     367: 	 * Give up. Can't reach destination.
     368: 	 */
     369:     hp = (HEADER *)(qp->q_cmsg ? qp->q_cmsg : qp->q_msg);
     370:     if (qp->q_system == PRIMING_CACHE) {
     371:         /* Can't give up priming */
     372:         unsched(qp);
     373:         schedretry(qp, (time_t)60*60);  /* 1 hour */
     374:         hp->rcode = NOERROR;    /* Lets be safe, reset the query */
     375:         hp->qr = hp->aa = 0;
     376:         qp->q_fwd = fwdtab;
     377:         for (n = 0; n < qp->q_naddr; n++)
     378:             qp->q_addr[n].nretry = 0;
     379:         return;
     380:     }
     381: #ifdef DEBUG
     382:     if (debug >= 5)
     383:         fprintf(ddt,"give up\n");
     384: #endif
     385:     n = ((HEADER *)qp->q_cmsg ? qp->q_cmsglen : qp->q_msglen);
     386:     hp->id = qp->q_id;
     387:     hp->qr = 1;
     388:     hp->ra = 1;
     389:     hp->rd = 1;
     390:     hp->rcode = SERVFAIL;
     391: #ifdef DEBUG
     392:     if (debug >= 10)
     393:         fp_query(qp->q_msg, ddt);
     394: #endif
     395:     if (send_msg((char *)hp, n, qp)) {
     396: #ifdef DEBUG
     397:         if (debug)
     398:             fprintf(ddt,"gave up retry(x%x) nsid=%d id=%d\n",
     399:                 qp, ntohs(qp->q_nsid), ntohs(qp->q_id));
     400: #endif
     401:     }
     402:     qremove(qp);
     403:     return;
     404:
     405: found:
     406:     if (qp->q_fwd == 0 && qp->q_addr[n].nretry == 0)
     407:         qp->q_addr[n].stime = tt;
     408:     qp->q_curaddr = n;
     409:     hp = (HEADER *)qp->q_msg;
     410:     hp->rd = (qp->q_fwd ? 1 : 0);
     411: #ifdef DEBUG
     412:     if (debug)
     413:         fprintf(ddt,"%s(addr=%d n=%d) -> %s %d (%d) nsid=%d id=%d %ldms\n",
     414:             (qp->q_fwd ? "reforw" : "resend"),
     415:             n, qp->q_addr[n].nretry,
     416:             inet_ntoa(Q_NEXTADDR(qp,n)->sin_addr),
     417:             ds, ntohs(Q_NEXTADDR(qp,n)->sin_port),
     418:             ntohs(qp->q_nsid), ntohs(qp->q_id),
     419:             qp->q_addr[n].nsdata->d_nstime);
     420:     if ( debug >= 10)
     421:         fp_query(qp->q_msg, ddt);
     422: #endif
     423:     /* NOSTRICT */
     424:     if (sendto(ds, qp->q_msg, qp->q_msglen, 0,
     425:         (struct sockaddr *)Q_NEXTADDR(qp,n),
     426:         sizeof(struct sockaddr_in)) < 0){
     427: #ifdef DEBUG
     428:         if (debug > 3)
     429:             fprintf(ddt,"error resending msg errno=%d\n",errno);
     430: #endif
     431:     }
     432:     hp->rd = 0; /* leave set to 0 for dup detection */
     433: #ifdef STATS
     434:     stats[S_OUTPKTS].cnt++;
     435: #endif
     436:     unsched(qp);
     437:     schedretry(qp, (time_t)(qp->q_fwd ? (2*RETRYBASE) : retrytime(qp)));
     438: }
     439:
     440: /*
     441:  * Compute retry time for the next server for a query.
     442:  * Use a minimum time of RETRYBASE (4 sec.) or twice the estimated
     443:  * service time; * back off exponentially on retries, but place a 45-sec.
     444:  * ceiling on retry times for now.  (This is because we don't hold a reference
     445:  * on servers or their addresses, and we have to finish before they time out.)
     446:  */
     447: time_t
     448: retrytime(qp)
     449: register struct qinfo *qp;
     450: {
     451:     time_t t;
     452:     struct qserv *ns = &qp->q_addr[qp->q_curaddr];
     453:
     454: #ifdef DEBUG
     455:     if (debug > 3)
     456:         fprintf(ddt,"retrytime: nstime %ldms.\n",
     457:             ns->nsdata->d_nstime / 1000);
     458: #endif
     459:     t = (time_t) MAX(RETRYBASE, 2 * ns->nsdata->d_nstime / 1000);
     460:     t <<= ns->nretry;
     461:     t = MIN(t, 45);         /* max. retry timeout for now */
     462: #ifdef notdef
     463:     if (qp->q_system)
     464:         return ((2 * t) + 5);   /* system queries can wait. */
     465: #endif
     466:     return (t);
     467: }
     468:
     469: qflush()
     470: {
     471:     while (qhead)
     472:         qremove(qhead);
     473:     qhead = QINFO_NULL;
     474: }
     475:
     476: qremove(qp)
     477: register struct qinfo *qp;
     478: {
     479: #ifdef DEBUG
     480:     if(debug > 3)
     481:         fprintf(ddt,"qremove(x%x)\n", qp);
     482: #endif
     483:     unsched(qp);            /* get off queue first */
     484:     qfree(qp);
     485: }
     486:
     487: struct qinfo *
     488: qfindid(id)
     489: register u_short id;
     490: {
     491:     register struct qinfo *qp;
     492:
     493: #ifdef DEBUG
     494:     if(debug > 3)
     495:         fprintf(ddt,"qfindid(%d)\n", ntohs(id));
     496: #endif
     497:     for (qp = qhead; qp!=QINFO_NULL; qp = qp->q_link) {
     498:         if (qp->q_nsid == id)
     499:             return(qp);
     500:     }
     501: #ifdef DEBUG
     502:     if (debug >= 5)
     503:         fprintf(ddt,"qp not found\n");
     504: #endif
     505:     return(NULL);
     506: }
     507:
     508: struct qinfo *
     509: qnew()
     510: {
     511:     register struct qinfo *qp;
     512:
     513:     if ((qp = (struct qinfo *)calloc(1, sizeof(struct qinfo))) == NULL) {
     514: #ifdef DEBUG
     515:         if (debug >= 5)
     516:             fprintf(ddt,"qnew: calloc error\n");
     517: #endif
     518:         syslog(LOG_ERR, "forw: %m");
     519:         exit(12);
     520:     }
     521: #ifdef DEBUG
     522:     if (debug >= 5)
     523:         fprintf(ddt,"qnew(x%x)\n", qp);
     524: #endif
     525:     qp->q_link = qhead;
     526:     qhead = qp;
     527:     return( qp );
     528: }
     529:
     530: qfree(qp)
     531: struct qinfo *qp;
     532: {
     533:     register struct qinfo *np;
     534:
     535: #ifdef DEBUG
     536:     if(debug > 3)
     537:         fprintf(ddt,"qfree( x%x )\n", qp);
     538:     if(debug && qp->q_next)
     539:         fprintf(ddt,"WARNING:  qfree of linked ptr x%x\n", qp);
     540: #endif
     541:     if (qp->q_msg)
     542:         free(qp->q_msg);
     543:     if (qp->q_cmsg)
     544:         free(qp->q_cmsg);
     545:     if( qhead == qp )  {
     546:         qhead = qp->q_link;
     547:     } else {
     548:         for( np=qhead; np->q_link != QINFO_NULL; np = np->q_link )  {
     549:             if( np->q_link != qp )  continue;
     550:             np->q_link = qp->q_link;    /* dequeue */
     551:             break;
     552:         }
     553:     }
     554:     (void)free((char *)qp);
     555: }

#### Defined functions

ns_forw defined in line 46; used 2 times

- in /usr/src/usr.sbin/named/named/ns_req.c line 333, 1149

nslookup defined in line 152; used 3 times

- in line 89
- in /usr/src/usr.sbin/named/named/ns_resp.c line 586, 1180

qcomp defined in line 265; used 2 times

- in line 164, 261

qfindid defined in line 487; used 2 times

- in /usr/src/usr.sbin/named/named/ns.h line 111
- in /usr/src/usr.sbin/named/named/ns_resp.c line 73

qflush defined in line 469; used 1 times

- in /usr/src/usr.sbin/named/named/db_reload.c line 42

qfree defined in line 530; used 6 times

- in line 94, 114, 484
- in /usr/src/usr.sbin/named/named/ns_resp.c line 1158, 1175, 1185

qnew defined in line 508; used 3 times

- in line 88
- in /usr/src/usr.sbin/named/named/ns.h line 112
- in /usr/src/usr.sbin/named/named/ns_resp.c line 1150

qremove defined in line 476; used 7 times

- in line 345, 402, 472
- in /usr/src/usr.sbin/named/named/ns_resp.c line 396, 676, 694, 708

retry defined in line 334; used 1 times

- in /usr/src/usr.sbin/named/named/ns_main.c line 637

retrytime defined in line 447; used 7 times

- in line 36, 119, 437
- in /usr/src/usr.sbin/named/named/ns_req.c line 46
- in /usr/src/usr.sbin/named/named/ns_resp.c line 32, 624, 1189

schedretry defined in line 277; used 5 times

- in line 119, 373, 437
- in /usr/src/usr.sbin/named/named/ns_resp.c line 624, 1189

unsched defined in line 307; used 4 times

- in line 372, 436, 483
- in /usr/src/usr.sbin/named/named/ns_resp.c line 623

#### Defined variables

fwdtab defined in line 29; used 4 times

- in line 100, 376
- in /usr/src/usr.sbin/named/named/ns_resp.c line 584, 1153

nsid defined in line 31; used 1 times

- in line 103

qhead defined in line 27; used 10 times

- in line 70, 471-473(3), 497, 525-526(2), 545-548(3)

retryqp defined in line 28; used 13 times

- in line 293-294(2), 317-320(3)
- in /usr/src/usr.sbin/named/named/ns_forw.c line 293-294(2), 317-320(3)
- in /usr/src/usr.sbin/named/named/ns_main.c line 633-637(3)

sccsid defined in line 14; never used

Last modified: 1988-09-12 Generated: 2016-12-26 | Generated by src2html V0.67 page hit count: 2553 |