Annotation of researchv8dc/sys/chaosld/chrcv.c, revision 1.1

1.1     ! root        1: /*
        !             2:  *             C H  R C V
        !             3:  *
        !             4:  * Chaosnet line discipline - handle a received packet.
        !             5:  * Chrcv() is called by the service routine for the RD queue.
        !             6:  *
        !             7:  *
        !             8:  * (c) Copyright 1984  Nirvonics, Inc.
        !             9:  *
        !            10:  * Written by Kurt Gollhardt
        !            11:  * Last update Fri Nov 16 11:16:20 1984
        !            12:  *
        !            13:  * This software is the property of Nirvonics, Inc.
        !            14:  * All rights reserved.
        !            15:  *
        !            16:  */
        !            17: 
        !            18: #include "ch.h"
        !            19: #if NCH > 0
        !            20: #include "../h/param.h"
        !            21: #include "../h/systm.h"
        !            22: #include "../h/stream.h"
        !            23: #include "../h/conf.h"
        !            24: #include "../chaosld/types.h"
        !            25: #include "../chaosld/constants.h"
        !            26: #include "../chaosld/globals.h"
        !            27: 
        !            28: #define send_los(pkt,str)     sendlos(pkt,str,sizeof(str)-1)
        !            29: 
        !            30: #define STS_AGE            hz/4      /* Minimum pkt age for re-xmission on STS */
        !            31: 
        !            32: 
        !            33: chrcv(q)
        !            34:      struct queue   *q;
        !            35: {
        !            36:      register struct packet        *pkt;
        !            37:      register struct connection    *conn;
        !            38:      register struct sts_data      *sts;
        !            39:      register unsigned   index;
        !            40: 
        !            41:      debug(DPKT,printf("Chrcv: "));
        !            42:      if ((pkt = get_packet(q, 0)) == NOPKT)
        !            43:           return;
        !            44:      pkt->next = NOPKT;
        !            45: 
        !            46:      ch_busy++;
        !            47:      if (pkt->pk_op == MNTOP)
        !            48:           free_packet(pkt);
        !            49:      else if (pkt->pk_op == RFCOP)
        !            50:           rcvrfc(pkt);
        !            51:      else if (pkt->pk_op == BRDOP)
        !            52:           rcvbrd(pkt);
        !            53:      /*
        !            54:       * Check for various flavors of bad indices
        !            55:       */
        !            56:      else if ((index = pkt->pk_didx.ci_tidx) >= NCH ||
        !            57:                (conn = Chconn[index]) == NOCONN ||
        !            58:               pkt->pk_didx.ci_idx != conn->cn_lidx.ci_idx) {
        !            59:           debug(DPKT|DABNOR,printf("Packet with bad index: %x, op:0%o\n",
        !            60:                pkt->pk_didx, pkt->pk_op));
        !            61:           send_los(pkt, "Connection doesn't exist");
        !            62:      /*
        !            63:       * Handle responses to our RFC
        !            64:       */
        !            65:      } else if (conn->cn_state == CSRFCSENT)
        !            66:           switch(pkt->pk_op) {
        !            67:           case OPNOP:
        !            68:                debug(DCONN,printf("Conn #%x: OPEN received\n", conn->cn_lidx));
        !            69:                /*
        !            70:                 * Mark the connection as open, saving his index
        !            71:                 */
        !            72:                conn->cn_state = CSOPEN;
        !            73:                conn->cn_fidx = pkt->pk_sidx;
        !            74:                conn->cn_time = Chclock;
        !            75:                /*
        !            76:                 * Indicate we have received AND "read" the OPEN
        !            77:                 * Read his window size, indicate that he has
        !            78:                 * received and acked the RFC, and acknowledge the OPN
        !            79:                 */
        !            80:                conn->cn_rread = conn->cn_rlast = pkt->pk_pkn;
        !            81:               flatten(pkt);
        !            82:               sts = (struct sts_data *)pkt->data->rptr;
        !            83:                conn->cn_twsize = sts->sts_rwsize;
        !            84:                receipt(conn, pkt->pk_ackn, pkt->pk_ackn);
        !            85:                /*
        !            86:                 * Send him back an STS to get things going.
        !            87:                 */
        !            88:                setsts(conn, pkt);
        !            89:                reflect(pkt);
        !            90:               chd_newstate(conn);
        !            91:                break;
        !            92:           case CLSOP:
        !            93:           case ANSOP:
        !            94:           case FWDOP:
        !            95:                debug(DCONN,printf("Conn #%x: CLOSE/ANS received for RFC\n", conn->cn_lidx));
        !            96:                close_conn(conn, CSCLOSED, pkt);
        !            97:                break;
        !            98:           default:
        !            99:                debug(DPKT|DABNOR,
        !           100:                     printf("bad packet type for conn #%x in CSRFCSENT: %d\n",
        !           101:                          conn->cn_lidx, pkt->pk_op));
        !           102:                send_los(pkt, "Bad packet type reponse to RFC");
        !           103:           }
        !           104:      /*
        !           105:       * Process a packet for an open connection
        !           106:       */
        !           107:      else if (conn->cn_state == CSOPEN) {
        !           108:           conn->cn_time = Chclock;
        !           109:           if (ISDATA(pkt))
        !           110:                rcvdata(conn, pkt);
        !           111:           else switch (pkt->pk_op) {
        !           112:               case OPNOP:
        !           113:                /*
        !           114:                 * Ignore duplicate opens.
        !           115:                 */
        !           116:                debug(DPKT|DABNOR,printf("Duplicate open received\n"));
        !           117:                free_packet(pkt);
        !           118:                break;
        !           119:           case SNSOP:
        !           120:                debug(DPKT,prpkt(pkt, "SNS"));
        !           121:                receipt(conn, pkt->pk_ackn, pkt->pk_ackn);
        !           122:                setsts(conn, pkt);
        !           123:                reflect(pkt);
        !           124:                break;
        !           125:           case EOFOP:
        !           126:                rcvdata(conn, pkt);
        !           127:                break;
        !           128:           case LOSOP:
        !           129:           case CLSOP:
        !           130:                debug(DCONN, printf("Close rcvd on %x\n", conn->cn_lidx));
        !           131:                close_conn(conn, pkt->pk_op == CLSOP ? CSCLOSED : CSLOST,
        !           132:                     pkt);
        !           133:                break;
        !           134:                /*
        !           135:                 * Uncontrolled data - queue it at the head of the rlist
        !           136:                 */
        !           137:           case UNCOP:
        !           138:                receipt(conn, pkt->pk_ackn, pkt->pk_ackn);
        !           139:               ch_input(conn, pkt);  /* Send packet to pseudo-device */
        !           140:                break;
        !           141:           case STSOP:
        !           142:                flatten(pkt);
        !           143:               sts = (struct sts_data *)pkt->data->rptr;
        !           144:                if (sts->sts_rwsize > conn->cn_twsize)
        !           145:                     chd_newout(conn);
        !           146:                conn->cn_twsize = sts->sts_rwsize;
        !           147:                receipt(conn, pkt->pk_ackn, sts->sts_receipt);
        !           148:                free_packet(pkt);
        !           149:               chretran(conn, STS_AGE);  /* Re-xmit all but very recent pkts */
        !           150:                break;
        !           151:           default:
        !           152:                debug(DPKT|DABNOR, printf("bad opcode:%d\n", pkt->pk_op));
        !           153:                send_los(pkt, "Bad opcode");
        !           154:                /* should we do close_conn here? */
        !           155:           }
        !           156:      /*
        !           157:       * Connection is neither waiting for an OPEN nor OPEN.
        !           158:       */
        !           159:      } else {
        !           160:           debug(DPKT|DABNOR,printf("Packet for conn #%x (not open) state=%d, op:%d\n", conn->cn_lidx, conn->cn_state, pkt->pk_op));
        !           161:           send_los(pkt, "Connection is closed");
        !           162:      }
        !           163: 
        !           164:      --ch_busy;
        !           165: }
        !           166: 
        !           167: /*
        !           168:  * Process a received data packet - or an EOF packet which is mostly treated
        !           169:  * the same.
        !           170:  */
        !           171: rcvdata(conn, pkt)
        !           172:      register struct connection    *conn;
        !           173:      register struct packet        *pkt;
        !           174: {
        !           175:      register struct packet   *npkt;
        !           176: 
        !           177:      debug(DPKT,(prpkt(pkt,"DATA"),printf("\n")) );
        !           178:      if (cmp_gt(pkt->pk_pkn, conn->cn_rread + conn->cn_rwsize)) {
        !           179:           debug(DPKT|DABNOR,printf("Discarding data out of window\n"));
        !           180:           free_packet(pkt);
        !           181:           return;
        !           182:      }
        !           183:      receipt(conn, pkt->pk_ackn, pkt->pk_ackn);
        !           184:      if (cmp_le(pkt->pk_pkn, conn->cn_rlast)) {
        !           185:           debug(DPKT,printf("Duplicate data packet\n"));
        !           186:           setsts(conn, pkt);
        !           187:           reflect(pkt);
        !           188:           return;
        !           189:      }
        !           190: 
        !           191:      /*
        !           192:       * Link the out of order list onto the new packet in case
        !           193:       * it fills the gap between in-order and out-of-order lists
        !           194:       * and to make it easy to grab all now-in-order packets from the
        !           195:       * out-of-order list.
        !           196:       */
        !           197:      pkt->next = conn->cn_routorder;
        !           198:      /*
        !           199:       * Now transfer all in-order packets to the pseudo-device
        !           200:       */
        !           201:      for (npkt = pkt; npkt != NOPKT &&
        !           202:                     npkt->pk_pkn == (unsigned short)(conn->cn_rlast + 1);
        !           203:                     npkt = npkt->next) {
        !           204:           conn->cn_rlast++;
        !           205:           ch_input(conn, npkt);
        !           206:      }
        !           207:      /*
        !           208:       * If we received any in-order pkts, check if spontaneous STS is needed
        !           209:       */
        !           210:      if (pkt != npkt) {
        !           211:           debug(DPKT,printf("new ordered data packet\n"));
        !           212:           conn->cn_routorder = npkt;
        !           213:      } else {
        !           214:      /*
        !           215:       * Here we have received an out of order packet which must be
        !           216:       * inserted into the out-of-order queue, in packet number order. 
        !           217:       */
        !           218:           for (npkt = pkt; (npkt = npkt->next) != NOPKT &&
        !           219:                      cmp_gt(pkt->pk_pkn, npkt->pk_pkn); )
        !           220:                pkt->next = npkt;     /* save the last pkt here */
        !           221: 
        !           222:           if (npkt != NOPKT && pkt->pk_pkn == npkt->pk_pkn) {
        !           223:                debug(DPKT|DABNOR,
        !           224:                     printf("Duplicate out of order packet\n"));
        !           225:                pkt->next = NOPKT;
        !           226:                setsts(conn, pkt);
        !           227:                reflect(pkt);
        !           228:           } else {
        !           229:                if (npkt == conn->cn_routorder)
        !           230:                     conn->cn_routorder = pkt;
        !           231:                else
        !           232:                     pkt->next->next = pkt;
        !           233:                pkt->next = npkt;
        !           234:                debug(DPKT|DABNOR,
        !           235:                      printf("New out of order packet\n"));
        !           236:           }
        !           237:      }
        !           238: }
        !           239: 
        !           240: /*
        !           241:  * Process receipts and acknowledgements using recnum as the receipt.
        !           242:  */
        !           243: receipt(conn, acknum, recnum)
        !           244: register struct connection *conn;
        !           245: unsigned short acknum, recnum;
        !           246: {
        !           247:      register struct packet *pkt, *pktl;
        !           248: 
        !           249:      ch_busy++;
        !           250:      /*
        !           251:       * Process a receipt, freeing packets that we now know have been
        !           252:       * received.
        !           253:       */
        !           254:      if (cmp_gt(recnum, conn->cn_trecvd)) {
        !           255:           for (pktl = conn->cn_thead;
        !           256:                pktl != NOPKT && cmp_le(pktl->pk_pkn, recnum);
        !           257:                pktl = pkt) {
        !           258:                pkt = pktl->next;
        !           259:                free_packet(pktl);
        !           260:           }
        !           261:           if ((conn->cn_thead = pktl) == NOPKT)
        !           262:                conn->cn_ttail = NOPKT;
        !           263:           conn->cn_trecvd = recnum;
        !           264:      }
        !           265:      /*
        !           266:       * If the acknowledgement is new, update our idea of the
        !           267:       * latest acknowledged packet, and wakeup output that might be blocked
        !           268:       * on a full transmit window.
        !           269:       */
        !           270:      if (cmp_gt(acknum, conn->cn_tacked))
        !           271:           if (cmp_gt(acknum, conn->cn_tlast)) {
        !           272:                debug(DABNOR, (printf("Invalid acknowledgment(%d,%d)\n",
        !           273:                     acknum, conn->cn_tlast)));
        !           274:           } else {
        !           275:                conn->cn_tacked = acknum;
        !           276:                chd_newout(conn);
        !           277:           }
        !           278:      --ch_busy;
        !           279: }
        !           280: 
        !           281: 
        !           282: #ifdef BRDBRIDGE
        !           283: 
        !           284: int Chbrdbridge = 1;
        !           285: #define MAXBRD    8
        !           286: 
        !           287: #endif BRDBRIDGE
        !           288: 
        !           289: /*
        !           290:  * Process a received BRD
        !           291:  * The trickiness is that we must modify the bit map completely before
        !           292:  * sending it to anyone - thus we must remember all those to send it to.
        !           293:  */
        !           294: 
        !           295: rcvbrd(pkt)
        !           296:      register struct packet   *pkt;
        !           297: {
        !           298:      int       bitlen = pkt->pk_ackn;
        !           299: 
        !           300:           /* Here pk_ackn is # bytes in subnet bitmap */
        !           301:      if ((pkt->pk_len <= bitlen) || (bitlen >= CHNSUBNET/8)) {
        !           302:           free_packet(pkt);
        !           303:           return;
        !           304:      }
        !           305:      if (flatten(pkt))
        !           306:           return;
        !           307: 
        !           308: #ifdef BRDBRIDGE
        !           309:      if (Chbrdbridge) {
        !           310:           register struct chroute  *r;
        !           311:           register int   mask, i;
        !           312:           struct packet  *npkt;
        !           313:          chaddr    addrs[MAXBRD], *adp;
        !           314: 
        !           315:           bitp = pkt->data->rptr;
        !           316:           for (i = 0, r = Chroutetab; r < bitlen; i++)
        !           317:                for (mask = 1; mask & 0377; mask <<= 1, r++)
        !           318:                     if (r->rt_type == CHDIRECT && r->rt_cost < HIGH_COST &&
        !           319:                               *bitp & mask && adp < &addrs[MAXBRD]) {
        !           320:                          *bitp &= ~mask;
        !           321:                         *adp++ = r->rt_path.ifp->my_addr;
        !           322:                     }
        !           323:           while (adp > addrs) {                         
        !           324:                if ((npkt = new_packet()) == NOPKT)
        !           325:                     return;
        !           326:                npkt->ph = pkt->ph;
        !           327:                append_packet(npkt, pkt->data->rptr, pkt->pk_len);
        !           328:                npkt->pk_daddr = *--adp;
        !           329:                npkt->pk_daddr.ch_host = 0;
        !           330:               npkt->pk_pkn = 1;
        !           331:                sendctl(npkt);
        !           332:           }
        !           333:      }
        !           334: #endif BRDBRIDGE
        !           335: 
        !           336:      pkt->pk_lenword = pkt->pk_len - bitlen;
        !           337:      pkt->data->rptr += bitlen;
        !           338:      pkt->pk_ackn = 0;
        !           339:      rcvrfc(pkt);
        !           340: }
        !           341: 
        !           342: /*
        !           343:  * Process a received RFC
        !           344:  */
        !           345: rcvrfc(pkt)
        !           346:      register struct packet   *pkt;
        !           347: {
        !           348:      register struct connection    *conn, **conptr;
        !           349:      register struct service  *svp;
        !           350:      extern struct service    Chservice[];
        !           351:      struct packet  **opkt, *pktl;
        !           352: 
        !           353:      debug(DPKT,prpkt(pkt,"RFC/BRD"));
        !           354:      /*
        !           355:       * Check if this is a duplicate RFC, and if so throw it away,
        !           356:       * and retransmit the OPEN.
        !           357:       */
        !           358:      for (conptr = &Chconn[0]; conptr < &Chconn[NCH];)
        !           359:           if ((conn = *conptr++) != NOCONN &&
        !           360:                     conn->cn_fidx.ci_idx == pkt->pk_sidx.ci_idx &&
        !           361:                      conn->cn_faddr.ch_addr == pkt->pk_saddr.ch_addr) {
        !           362:                if (conn->cn_state == CSOPEN) {
        !           363:                     debug(DPKT|DABNOR,
        !           364:                          printf("Rcvrfc: Retransmitting open chan #%x\n",
        !           365:                                   (*conptr)->cn_lidx.ci_idx));
        !           366:                     chretran(conn, 0);
        !           367:                } else {
        !           368:                     debug(DPKT|DABNOR,
        !           369:                          printf("Rcvrfc: Duplicate RFC: %x\n",
        !           370:                               conn->cn_lidx.ci_idx));
        !           371:                }
        !           372:                free_packet(pkt);
        !           373:                return;
        !           374:           }
        !           375: 
        !           376:      if (flatten(pkt)) {
        !           377:           free_packet(pkt);
        !           378:          return;
        !           379:      }
        !           380: 
        !           381:      /*
        !           382:       * Scan the listen list for a listener and if one is found
        !           383:       * open the connection and remove the listen packet from the
        !           384:       * listen list.
        !           385:       */
        !           386:      for (opkt = &Chlsnlist; (pktl = *opkt) != NOPKT; opkt = &pktl->next)
        !           387:           if(concmp(pkt, pktl->data->rptr, (int)pktl->pk_len)) {
        !           388:                conn = Chconn[pktl->pk_sidx.ci_tidx];
        !           389:                *opkt = pktl->next;
        !           390:                free_packet(pktl);
        !           391:                lsnmatch(pkt, conn);
        !           392:                chd_newstate(conn);
        !           393:                return;
        !           394:           }
        !           395:      /*
        !           396:       * Check for built-in services.
        !           397:       */
        !           398:      for (svp = Chservice; svp->name != 0; ++svp)
        !           399:           if (concmp(pkt, svp->name, svp->len)) {
        !           400:               (*svp->func)(pkt);
        !           401:               return;
        !           402:           }
        !           403:      /*
        !           404:       * There was no listener, so queue the RFC on the unmatched RFC list
        !           405:       * again checking for duplicates.
        !           406:       */
        !           407:      if ((pktl = Chrfclist) == NOPKT) 
        !           408:           Chrfclist = Chrfctail = pkt;
        !           409:      else {
        !           410:           do {
        !           411:                if(pktl->pk_sidx.ci_idx == pkt->pk_sidx.ci_idx &&
        !           412:                   pktl->pk_saddr.ch_addr == pkt->pk_saddr.ch_addr) {
        !           413:                     debug(DPKT/*|DABNOR*/,printf("Rcvrfc: Discarding duplicate Rfc on Chrfclist\n"));
        !           414:                     free_packet(pkt);
        !           415:                     return;
        !           416:                }
        !           417:           } while ((pktl = pktl->next) != NOPKT);
        !           418:           Chrfctail->next = pkt;
        !           419:           Chrfctail = pkt;
        !           420:      }
        !           421:      debug(DCONN,printf("Rcvrfc: Queued Rfc on Chrfclist\n"));
        !           422:      /*
        !           423:       * A new unmatched RFC has been received.  Send a copy of it up to the
        !           424:       * unmatched RFC server.
        !           425:       */
        !           426:      copy_pkdata(ChaosQ, pkt);
        !           427: }
        !           428: 
        !           429: /*
        !           430:  * An RFC has matched a listener, either by an RFC coming and finding a match
        !           431:  * on the listener list, or by a listen being done and matching an RFC on the
        !           432:  * unmatched RFC list. So we change the state of the connection to CSRFCRCVD
        !           433:  */
        !           434: lsnmatch(rfcpkt, conn)
        !           435:      register struct packet        *rfcpkt;
        !           436:      register struct connection    *conn;
        !           437: {
        !           438:      debug(DCONN,printf("Conn #%x: LISTEN matched \n", conn->cn_lidx));
        !           439:      /*
        !           440:       * Initialize the conection
        !           441:       */
        !           442:      conn->cn_time = Chclock;
        !           443:      conn->cn_state = CSRFCRCVD;
        !           444:      if (conn->cn_rwsize == 0)
        !           445:           conn->cn_rwsize = CHDRWSIZE;
        !           446:      conn->cn_faddr = rfcpkt->pk_saddr;
        !           447:      conn->cn_fidx = rfcpkt->pk_sidx;
        !           448: 
        !           449:      /*
        !           450:       * Indicate to the other end that we have received and "read"
        !           451:       * the RFC so that the open will truly acknowledge it.
        !           452:       */
        !           453:      conn->cn_rlast = conn->cn_rread = rfcpkt->pk_pkn;
        !           454:      /*
        !           455:       * Queue up the RFC for the user to read if he wants it.
        !           456:       */
        !           457:      chdrint(conn, rfcpkt);
        !           458: }
        !           459: 
        !           460: /*
        !           461:  * Remove a listener from the listener list, due to the listener bailing out.
        !           462:  * Called from top level at high priority
        !           463:  */
        !           464: rmlisten(conn)
        !           465:      register struct connection    *conn;
        !           466: {
        !           467:      register struct packet   *opkt, *pkt;
        !           468: 
        !           469:      opkt = NOPKT;
        !           470:      for (pkt = Chlsnlist; pkt != NOPKT; opkt = pkt, pkt = pkt->next)
        !           471:           if (pkt->pk_sidx.ci_tidx == conn->cn_lidx.ci_tidx) {
        !           472:                if(opkt == NOPKT)
        !           473:                     Chlsnlist = pkt->next;
        !           474:                else
        !           475:                     opkt->next = pkt->next;
        !           476:                free_packet(pkt);
        !           477:                break;
        !           478:           }
        !           479: }
        !           480: 
        !           481: /*
        !           482:  * Compare the RFC contact name with the listener name.
        !           483:  */
        !           484: concmp(rfcpkt, lsnstr, lsnlen)
        !           485:      struct packet  *rfcpkt;
        !           486:      register char  *lsnstr;
        !           487:      register int   lsnlen;
        !           488: {
        !           489:      register char *rfcstr = (char *)rfcpkt->data->rptr;
        !           490:      register int rfclen;
        !           491: 
        !           492:      debug(DPKT,{printf("Rcvrfc: Comparing ");
        !           493:                  print_str(rfcpkt->pk_len, rfcstr);
        !           494:                 printf(" and ");
        !           495:                 print_str(lsnlen, lsnstr);
        !           496:                 printf("\n");});
        !           497:      for (rfclen = rfcpkt->pk_len; rfclen; rfclen--, lsnlen--)
        !           498:           if (lsnlen <= 0)
        !           499:                return ((*rfcstr == ' ') ? 1 : 0);
        !           500:           else if (*rfcstr++ != *lsnstr++)
        !           501:                return(0);
        !           502:      return (lsnlen == 0);
        !           503: }
        !           504: 
        !           505: 
        !           506: ch_input(conn, pkt)
        !           507:      struct connection   *conn;
        !           508:      struct packet       *pkt;
        !           509: {
        !           510:      int       pkn, op, controlled;
        !           511: 
        !           512:      pkn = pkt->pk_pkn;
        !           513:      op = pkt->pk_op;
        !           514:      controlled = CONTROLLED(pkt);
        !           515: 
        !           516:      chdrint(conn, pkt);
        !           517: 
        !           518:      if (controlled) {
        !           519:           conn->cn_rread = pkn;
        !           520:          if (op == EOFOP)
        !           521:               sendsts(conn);
        !           522:      }
        !           523: }
        !           524: 
        !           525: 
        !           526: #ifdef DEBUG
        !           527: prpkt(pkt, str)
        !           528: register struct packet *pkt;
        !           529: char *str;
        !           530: {
        !           531:      printf("op=%s(%o) len=%d fc=%d\ndhost=%o didx=%x\nshost=%o sidx=%x\npkn=%d ackn=%d\n",
        !           532:           str, pkt->pk_op, pkt->pk_len, pkt->pk_fc, pkt->pk_daddr.ch_addr,
        !           533:           pkt->pk_didx.ci_idx, pkt->pk_saddr.ch_addr, pkt->pk_sidx.ci_idx,
        !           534:           (unsigned)pkt->pk_pkn, (unsigned)pkt->pk_ackn);
        !           535: }
        !           536: 
        !           537: print_str(len, s)
        !           538:      register int   len;
        !           539:      register char  *s;
        !           540: {
        !           541:      while (len-- > 0 && *s != '\0')
        !           542:           printf("%c", *s++);
        !           543: }
        !           544: #endif DEBUG
        !           545: 
        !           546: #endif

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.