Annotation of qemu/slirp/slirp.c, revision 1.1.1.5

1.1.1.5 ! root        1: /*
        !             2:  * libslirp glue
        !             3:  *
        !             4:  * Copyright (c) 2004-2008 Fabrice Bellard
        !             5:  *
        !             6:  * Permission is hereby granted, free of charge, to any person obtaining a copy
        !             7:  * of this software and associated documentation files (the "Software"), to deal
        !             8:  * in the Software without restriction, including without limitation the rights
        !             9:  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        !            10:  * copies of the Software, and to permit persons to whom the Software is
        !            11:  * furnished to do so, subject to the following conditions:
        !            12:  *
        !            13:  * The above copyright notice and this permission notice shall be included in
        !            14:  * all copies or substantial portions of the Software.
        !            15:  *
        !            16:  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        !            17:  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        !            18:  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
        !            19:  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        !            20:  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        !            21:  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
        !            22:  * THE SOFTWARE.
        !            23:  */
        !            24: #include "qemu-common.h"
        !            25: #include "qemu-char.h"
1.1       root       26: #include "slirp.h"
1.1.1.5 ! root       27: #include "hw/hw.h"
1.1       root       28: 
                     29: /* host address */
                     30: struct in_addr our_addr;
                     31: /* host dns address */
                     32: struct in_addr dns_addr;
                     33: /* host loopback address */
                     34: struct in_addr loopback_addr;
                     35: 
                     36: /* address for slirp virtual addresses */
                     37: struct in_addr special_addr;
1.1.1.2   root       38: /* virtual address alias for host */
                     39: struct in_addr alias_addr;
1.1       root       40: 
1.1.1.4   root       41: static const uint8_t special_ethaddr[6] = {
1.1       root       42:     0x52, 0x54, 0x00, 0x12, 0x35, 0x00
                     43: };
                     44: 
1.1.1.5 ! root       45: /* ARP cache for the guest IP addresses (XXX: allow many entries) */
1.1       root       46: uint8_t client_ethaddr[6];
1.1.1.5 ! root       47: static struct in_addr client_ipaddr;
        !            48: 
        !            49: static const uint8_t zero_ethaddr[6] = { 0, 0, 0, 0, 0, 0 };
1.1       root       50: 
1.1.1.5 ! root       51: const char *slirp_special_ip = CTL_SPECIAL;
        !            52: int slirp_restrict;
1.1       root       53: int do_slowtimo;
                     54: int link_up;
                     55: struct timeval tt;
                     56: FILE *lfd;
                     57: struct ex_list *exec_list;
                     58: 
                     59: /* XXX: suppress those select globals */
                     60: fd_set *global_readfds, *global_writefds, *global_xfds;
                     61: 
1.1.1.2   root       62: char slirp_hostname[33];
                     63: 
1.1       root       64: #ifdef _WIN32
                     65: 
                     66: static int get_dns_addr(struct in_addr *pdns_addr)
                     67: {
                     68:     FIXED_INFO *FixedInfo=NULL;
                     69:     ULONG    BufLen;
                     70:     DWORD    ret;
                     71:     IP_ADDR_STRING *pIPAddr;
                     72:     struct in_addr tmp_addr;
1.1.1.4   root       73: 
1.1       root       74:     FixedInfo = (FIXED_INFO *)GlobalAlloc(GPTR, sizeof(FIXED_INFO));
                     75:     BufLen = sizeof(FIXED_INFO);
1.1.1.4   root       76: 
1.1       root       77:     if (ERROR_BUFFER_OVERFLOW == GetNetworkParams(FixedInfo, &BufLen)) {
                     78:         if (FixedInfo) {
                     79:             GlobalFree(FixedInfo);
                     80:             FixedInfo = NULL;
                     81:         }
                     82:         FixedInfo = GlobalAlloc(GPTR, BufLen);
                     83:     }
1.1.1.4   root       84: 
1.1       root       85:     if ((ret = GetNetworkParams(FixedInfo, &BufLen)) != ERROR_SUCCESS) {
                     86:         printf("GetNetworkParams failed. ret = %08x\n", (u_int)ret );
                     87:         if (FixedInfo) {
                     88:             GlobalFree(FixedInfo);
                     89:             FixedInfo = NULL;
                     90:         }
                     91:         return -1;
                     92:     }
1.1.1.4   root       93: 
1.1       root       94:     pIPAddr = &(FixedInfo->DnsServerList);
                     95:     inet_aton(pIPAddr->IpAddress.String, &tmp_addr);
                     96:     *pdns_addr = tmp_addr;
                     97: #if 0
                     98:     printf( "DNS Servers:\n" );
                     99:     printf( "DNS Addr:%s\n", pIPAddr->IpAddress.String );
1.1.1.4   root      100: 
1.1       root      101:     pIPAddr = FixedInfo -> DnsServerList.Next;
                    102:     while ( pIPAddr ) {
                    103:             printf( "DNS Addr:%s\n", pIPAddr ->IpAddress.String );
                    104:             pIPAddr = pIPAddr ->Next;
                    105:     }
                    106: #endif
                    107:     if (FixedInfo) {
                    108:         GlobalFree(FixedInfo);
                    109:         FixedInfo = NULL;
                    110:     }
                    111:     return 0;
                    112: }
                    113: 
                    114: #else
                    115: 
                    116: static int get_dns_addr(struct in_addr *pdns_addr)
                    117: {
                    118:     char buff[512];
1.1.1.5 ! root      119:     char buff2[257];
1.1       root      120:     FILE *f;
                    121:     int found = 0;
                    122:     struct in_addr tmp_addr;
1.1.1.4   root      123: 
1.1       root      124:     f = fopen("/etc/resolv.conf", "r");
                    125:     if (!f)
                    126:         return -1;
                    127: 
1.1.1.4   root      128: #ifdef DEBUG
1.1       root      129:     lprint("IP address of your DNS(s): ");
1.1.1.4   root      130: #endif
1.1       root      131:     while (fgets(buff, 512, f) != NULL) {
                    132:         if (sscanf(buff, "nameserver%*[ \t]%256s", buff2) == 1) {
                    133:             if (!inet_aton(buff2, &tmp_addr))
                    134:                 continue;
                    135:             if (tmp_addr.s_addr == loopback_addr.s_addr)
                    136:                 tmp_addr = our_addr;
                    137:             /* If it's the first one, set it to dns_addr */
                    138:             if (!found)
                    139:                 *pdns_addr = tmp_addr;
1.1.1.4   root      140: #ifdef DEBUG
1.1       root      141:             else
                    142:                 lprint(", ");
1.1.1.4   root      143: #endif
1.1       root      144:             if (++found > 3) {
1.1.1.4   root      145: #ifdef DEBUG
1.1       root      146:                 lprint("(more)");
1.1.1.4   root      147: #endif
1.1       root      148:                 break;
1.1.1.4   root      149:             }
                    150: #ifdef DEBUG
                    151:             else
1.1       root      152:                 lprint("%s", inet_ntoa(tmp_addr));
1.1.1.4   root      153: #endif
1.1       root      154:         }
                    155:     }
                    156:     fclose(f);
                    157:     if (!found)
                    158:         return -1;
                    159:     return 0;
                    160: }
                    161: 
                    162: #endif
                    163: 
                    164: #ifdef _WIN32
1.1.1.4   root      165: static void slirp_cleanup(void)
1.1       root      166: {
                    167:     WSACleanup();
                    168: }
                    169: #endif
                    170: 
1.1.1.5 ! root      171: static void slirp_state_save(QEMUFile *f, void *opaque);
        !           172: static int slirp_state_load(QEMUFile *f, void *opaque, int version_id);
        !           173: 
        !           174: void slirp_init(int restrict, char *special_ip)
1.1       root      175: {
                    176:     //    debug_init("/tmp/slirp.log", DEBUG_DEFAULT);
1.1.1.4   root      177: 
1.1       root      178: #ifdef _WIN32
                    179:     {
                    180:         WSADATA Data;
                    181:         WSAStartup(MAKEWORD(2,0), &Data);
                    182:        atexit(slirp_cleanup);
                    183:     }
                    184: #endif
                    185: 
                    186:     link_up = 1;
1.1.1.5 ! root      187:     slirp_restrict = restrict;
1.1       root      188: 
                    189:     if_init();
                    190:     ip_init();
                    191: 
                    192:     /* Initialise mbufs *after* setting the MTU */
                    193:     m_init();
                    194: 
                    195:     /* set default addresses */
                    196:     inet_aton("127.0.0.1", &loopback_addr);
                    197: 
                    198:     if (get_dns_addr(&dns_addr) < 0) {
1.1.1.2   root      199:         dns_addr = loopback_addr;
                    200:         fprintf (stderr, "Warning: No DNS servers found\n");
1.1       root      201:     }
                    202: 
1.1.1.5 ! root      203:     if (special_ip)
        !           204:         slirp_special_ip = special_ip;
        !           205: 
        !           206:     inet_aton(slirp_special_ip, &special_addr);
1.1.1.2   root      207:     alias_addr.s_addr = special_addr.s_addr | htonl(CTL_ALIAS);
                    208:     getouraddr();
1.1.1.5 ! root      209:     register_savevm("slirp", 0, 1, slirp_state_save, slirp_state_load, NULL);
1.1       root      210: }
                    211: 
                    212: #define CONN_CANFSEND(so) (((so)->so_state & (SS_FCANTSENDMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
                    213: #define CONN_CANFRCV(so) (((so)->so_state & (SS_FCANTRCVMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
                    214: #define UPD_NFDS(x) if (nfds < (x)) nfds = (x)
                    215: 
                    216: /*
                    217:  * curtime kept to an accuracy of 1ms
                    218:  */
                    219: #ifdef _WIN32
                    220: static void updtime(void)
                    221: {
                    222:     struct _timeb tb;
                    223: 
                    224:     _ftime(&tb);
                    225:     curtime = (u_int)tb.time * (u_int)1000;
                    226:     curtime += (u_int)tb.millitm;
                    227: }
                    228: #else
                    229: static void updtime(void)
                    230: {
                    231:        gettimeofday(&tt, 0);
1.1.1.4   root      232: 
1.1       root      233:        curtime = (u_int)tt.tv_sec * (u_int)1000;
                    234:        curtime += (u_int)tt.tv_usec / (u_int)1000;
1.1.1.4   root      235: 
1.1       root      236:        if ((tt.tv_usec % 1000) >= 500)
                    237:           curtime++;
                    238: }
                    239: #endif
                    240: 
1.1.1.4   root      241: void slirp_select_fill(int *pnfds,
1.1       root      242:                        fd_set *readfds, fd_set *writefds, fd_set *xfds)
                    243: {
                    244:     struct socket *so, *so_next;
                    245:     struct timeval timeout;
                    246:     int nfds;
                    247:     int tmp_time;
                    248: 
                    249:     /* fail safe */
                    250:     global_readfds = NULL;
                    251:     global_writefds = NULL;
                    252:     global_xfds = NULL;
1.1.1.4   root      253: 
1.1       root      254:     nfds = *pnfds;
                    255:        /*
                    256:         * First, TCP sockets
                    257:         */
                    258:        do_slowtimo = 0;
                    259:        if (link_up) {
1.1.1.4   root      260:                /*
1.1       root      261:                 * *_slowtimo needs calling if there are IP fragments
                    262:                 * in the fragment queue, or there are TCP connections active
                    263:                 */
                    264:                do_slowtimo = ((tcb.so_next != &tcb) ||
1.1.1.5 ! root      265:                 (&ipq.ip_link != ipq.ip_link.next));
1.1.1.4   root      266: 
1.1       root      267:                for (so = tcb.so_next; so != &tcb; so = so_next) {
                    268:                        so_next = so->so_next;
1.1.1.4   root      269: 
1.1       root      270:                        /*
                    271:                         * See if we need a tcp_fasttimo
                    272:                         */
                    273:                        if (time_fasttimo == 0 && so->so_tcpcb->t_flags & TF_DELACK)
                    274:                           time_fasttimo = curtime; /* Flag when we want a fasttimo */
1.1.1.4   root      275: 
1.1       root      276:                        /*
                    277:                         * NOFDREF can include still connecting to local-host,
                    278:                         * newly socreated() sockets etc. Don't want to select these.
                    279:                         */
                    280:                        if (so->so_state & SS_NOFDREF || so->s == -1)
                    281:                           continue;
1.1.1.4   root      282: 
1.1       root      283:                        /*
                    284:                         * Set for reading sockets which are accepting
                    285:                         */
                    286:                        if (so->so_state & SS_FACCEPTCONN) {
                    287:                                 FD_SET(so->s, readfds);
                    288:                                UPD_NFDS(so->s);
                    289:                                continue;
                    290:                        }
1.1.1.4   root      291: 
1.1       root      292:                        /*
                    293:                         * Set for writing sockets which are connecting
                    294:                         */
                    295:                        if (so->so_state & SS_ISFCONNECTING) {
                    296:                                FD_SET(so->s, writefds);
                    297:                                UPD_NFDS(so->s);
                    298:                                continue;
                    299:                        }
1.1.1.4   root      300: 
1.1       root      301:                        /*
                    302:                         * Set for writing if we are connected, can send more, and
                    303:                         * we have something to send
                    304:                         */
                    305:                        if (CONN_CANFSEND(so) && so->so_rcv.sb_cc) {
                    306:                                FD_SET(so->s, writefds);
                    307:                                UPD_NFDS(so->s);
                    308:                        }
1.1.1.4   root      309: 
1.1       root      310:                        /*
                    311:                         * Set for reading (and urgent data) if we are connected, can
                    312:                         * receive more, and we have room for it XXX /2 ?
                    313:                         */
                    314:                        if (CONN_CANFRCV(so) && (so->so_snd.sb_cc < (so->so_snd.sb_datalen/2))) {
                    315:                                FD_SET(so->s, readfds);
                    316:                                FD_SET(so->s, xfds);
                    317:                                UPD_NFDS(so->s);
                    318:                        }
                    319:                }
1.1.1.4   root      320: 
1.1       root      321:                /*
                    322:                 * UDP sockets
                    323:                 */
                    324:                for (so = udb.so_next; so != &udb; so = so_next) {
                    325:                        so_next = so->so_next;
1.1.1.4   root      326: 
1.1       root      327:                        /*
                    328:                         * See if it's timed out
                    329:                         */
                    330:                        if (so->so_expire) {
                    331:                                if (so->so_expire <= curtime) {
                    332:                                        udp_detach(so);
                    333:                                        continue;
                    334:                                } else
                    335:                                        do_slowtimo = 1; /* Let socket expire */
                    336:                        }
1.1.1.4   root      337: 
1.1       root      338:                        /*
                    339:                         * When UDP packets are received from over the
                    340:                         * link, they're sendto()'d straight away, so
                    341:                         * no need for setting for writing
                    342:                         * Limit the number of packets queued by this session
                    343:                         * to 4.  Note that even though we try and limit this
                    344:                         * to 4 packets, the session could have more queued
                    345:                         * if the packets needed to be fragmented
                    346:                         * (XXX <= 4 ?)
                    347:                         */
                    348:                        if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4) {
                    349:                                FD_SET(so->s, readfds);
                    350:                                UPD_NFDS(so->s);
                    351:                        }
                    352:                }
                    353:        }
1.1.1.4   root      354: 
1.1       root      355:        /*
                    356:         * Setup timeout to use minimum CPU usage, especially when idle
                    357:         */
1.1.1.4   root      358: 
                    359:        /*
1.1       root      360:         * First, see the timeout needed by *timo
                    361:         */
                    362:        timeout.tv_sec = 0;
                    363:        timeout.tv_usec = -1;
                    364:        /*
                    365:         * If a slowtimo is needed, set timeout to 500ms from the last
                    366:         * slow timeout. If a fast timeout is needed, set timeout within
                    367:         * 200ms of when it was requested.
                    368:         */
                    369:        if (do_slowtimo) {
                    370:                /* XXX + 10000 because some select()'s aren't that accurate */
                    371:                timeout.tv_usec = ((500 - (curtime - last_slowtimo)) * 1000) + 10000;
                    372:                if (timeout.tv_usec < 0)
                    373:                   timeout.tv_usec = 0;
                    374:                else if (timeout.tv_usec > 510000)
                    375:                   timeout.tv_usec = 510000;
1.1.1.4   root      376: 
1.1       root      377:                /* Can only fasttimo if we also slowtimo */
                    378:                if (time_fasttimo) {
                    379:                        tmp_time = (200 - (curtime - time_fasttimo)) * 1000;
                    380:                        if (tmp_time < 0)
                    381:                           tmp_time = 0;
1.1.1.4   root      382: 
1.1       root      383:                        /* Choose the smallest of the 2 */
                    384:                        if (tmp_time < timeout.tv_usec)
                    385:                           timeout.tv_usec = (u_int)tmp_time;
                    386:                }
                    387:        }
                    388:         *pnfds = nfds;
1.1.1.4   root      389: }
1.1       root      390: 
                    391: void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds)
                    392: {
                    393:     struct socket *so, *so_next;
                    394:     int ret;
                    395: 
                    396:     global_readfds = readfds;
                    397:     global_writefds = writefds;
                    398:     global_xfds = xfds;
                    399: 
                    400:        /* Update time */
                    401:        updtime();
1.1.1.4   root      402: 
1.1       root      403:        /*
1.1.1.4   root      404:         * See if anything has timed out
1.1       root      405:         */
                    406:        if (link_up) {
                    407:                if (time_fasttimo && ((curtime - time_fasttimo) >= 2)) {
                    408:                        tcp_fasttimo();
                    409:                        time_fasttimo = 0;
                    410:                }
                    411:                if (do_slowtimo && ((curtime - last_slowtimo) >= 499)) {
                    412:                        ip_slowtimo();
                    413:                        tcp_slowtimo();
                    414:                        last_slowtimo = curtime;
                    415:                }
                    416:        }
1.1.1.4   root      417: 
1.1       root      418:        /*
                    419:         * Check sockets
                    420:         */
                    421:        if (link_up) {
                    422:                /*
                    423:                 * Check TCP sockets
                    424:                 */
                    425:                for (so = tcb.so_next; so != &tcb; so = so_next) {
                    426:                        so_next = so->so_next;
1.1.1.4   root      427: 
1.1       root      428:                        /*
                    429:                         * FD_ISSET is meaningless on these sockets
                    430:                         * (and they can crash the program)
                    431:                         */
                    432:                        if (so->so_state & SS_NOFDREF || so->s == -1)
                    433:                           continue;
1.1.1.4   root      434: 
1.1       root      435:                        /*
                    436:                         * Check for URG data
                    437:                         * This will soread as well, so no need to
                    438:                         * test for readfds below if this succeeds
                    439:                         */
                    440:                        if (FD_ISSET(so->s, xfds))
                    441:                           sorecvoob(so);
                    442:                        /*
                    443:                         * Check sockets for reading
                    444:                         */
                    445:                        else if (FD_ISSET(so->s, readfds)) {
                    446:                                /*
                    447:                                 * Check for incoming connections
                    448:                                 */
                    449:                                if (so->so_state & SS_FACCEPTCONN) {
                    450:                                        tcp_connect(so);
                    451:                                        continue;
                    452:                                } /* else */
                    453:                                ret = soread(so);
1.1.1.4   root      454: 
1.1       root      455:                                /* Output it if we read something */
                    456:                                if (ret > 0)
                    457:                                   tcp_output(sototcpcb(so));
                    458:                        }
1.1.1.4   root      459: 
1.1       root      460:                        /*
                    461:                         * Check sockets for writing
                    462:                         */
                    463:                        if (FD_ISSET(so->s, writefds)) {
                    464:                          /*
                    465:                           * Check for non-blocking, still-connecting sockets
                    466:                           */
                    467:                          if (so->so_state & SS_ISFCONNECTING) {
                    468:                            /* Connected */
                    469:                            so->so_state &= ~SS_ISFCONNECTING;
1.1.1.4   root      470: 
1.1       root      471:                            ret = send(so->s, &ret, 0, 0);
                    472:                            if (ret < 0) {
                    473:                              /* XXXXX Must fix, zero bytes is a NOP */
                    474:                              if (errno == EAGAIN || errno == EWOULDBLOCK ||
                    475:                                  errno == EINPROGRESS || errno == ENOTCONN)
                    476:                                continue;
1.1.1.4   root      477: 
1.1       root      478:                              /* else failed */
                    479:                              so->so_state = SS_NOFDREF;
                    480:                            }
                    481:                            /* else so->so_state &= ~SS_ISFCONNECTING; */
1.1.1.4   root      482: 
1.1       root      483:                            /*
                    484:                             * Continue tcp_input
                    485:                             */
                    486:                            tcp_input((struct mbuf *)NULL, sizeof(struct ip), so);
                    487:                            /* continue; */
                    488:                          } else
                    489:                            ret = sowrite(so);
                    490:                          /*
1.1.1.4   root      491:                           * XXXXX If we wrote something (a lot), there
1.1       root      492:                           * could be a need for a window update.
                    493:                           * In the worst case, the remote will send
                    494:                           * a window probe to get things going again
                    495:                           */
                    496:                        }
1.1.1.4   root      497: 
1.1       root      498:                        /*
                    499:                         * Probe a still-connecting, non-blocking socket
                    500:                         * to check if it's still alive
                    501:                         */
                    502: #ifdef PROBE_CONN
                    503:                        if (so->so_state & SS_ISFCONNECTING) {
                    504:                          ret = recv(so->s, (char *)&ret, 0,0);
1.1.1.4   root      505: 
1.1       root      506:                          if (ret < 0) {
                    507:                            /* XXX */
                    508:                            if (errno == EAGAIN || errno == EWOULDBLOCK ||
                    509:                                errno == EINPROGRESS || errno == ENOTCONN)
                    510:                              continue; /* Still connecting, continue */
1.1.1.4   root      511: 
1.1       root      512:                            /* else failed */
                    513:                            so->so_state = SS_NOFDREF;
1.1.1.4   root      514: 
1.1       root      515:                            /* tcp_input will take care of it */
                    516:                          } else {
                    517:                            ret = send(so->s, &ret, 0,0);
                    518:                            if (ret < 0) {
                    519:                              /* XXX */
                    520:                              if (errno == EAGAIN || errno == EWOULDBLOCK ||
                    521:                                  errno == EINPROGRESS || errno == ENOTCONN)
                    522:                                continue;
                    523:                              /* else failed */
                    524:                              so->so_state = SS_NOFDREF;
                    525:                            } else
                    526:                              so->so_state &= ~SS_ISFCONNECTING;
1.1.1.4   root      527: 
1.1       root      528:                          }
                    529:                          tcp_input((struct mbuf *)NULL, sizeof(struct ip),so);
                    530:                        } /* SS_ISFCONNECTING */
                    531: #endif
                    532:                }
1.1.1.4   root      533: 
1.1       root      534:                /*
                    535:                 * Now UDP sockets.
                    536:                 * Incoming packets are sent straight away, they're not buffered.
                    537:                 * Incoming UDP data isn't buffered either.
                    538:                 */
                    539:                for (so = udb.so_next; so != &udb; so = so_next) {
                    540:                        so_next = so->so_next;
1.1.1.4   root      541: 
1.1       root      542:                        if (so->s != -1 && FD_ISSET(so->s, readfds)) {
                    543:                             sorecvfrom(so);
                    544:                         }
                    545:                }
                    546:        }
1.1.1.4   root      547: 
1.1       root      548:        /*
                    549:         * See if we can start outputting
                    550:         */
                    551:        if (if_queued && link_up)
                    552:           if_start();
                    553: 
                    554:        /* clear global file descriptor sets.
                    555:         * these reside on the stack in vl.c
                    556:         * so they're unusable if we're not in
                    557:         * slirp_select_fill or slirp_select_poll.
                    558:         */
                    559:         global_readfds = NULL;
                    560:         global_writefds = NULL;
                    561:         global_xfds = NULL;
                    562: }
                    563: 
                    564: #define ETH_ALEN 6
                    565: #define ETH_HLEN 14
                    566: 
                    567: #define ETH_P_IP       0x0800          /* Internet Protocol packet     */
                    568: #define ETH_P_ARP      0x0806          /* Address Resolution packet    */
                    569: 
                    570: #define        ARPOP_REQUEST   1               /* ARP request                  */
                    571: #define        ARPOP_REPLY     2               /* ARP reply                    */
                    572: 
1.1.1.4   root      573: struct ethhdr
1.1       root      574: {
                    575:        unsigned char   h_dest[ETH_ALEN];       /* destination eth addr */
                    576:        unsigned char   h_source[ETH_ALEN];     /* source ether addr    */
                    577:        unsigned short  h_proto;                /* packet type ID field */
                    578: };
                    579: 
                    580: struct arphdr
                    581: {
                    582:        unsigned short  ar_hrd;         /* format of hardware address   */
                    583:        unsigned short  ar_pro;         /* format of protocol address   */
                    584:        unsigned char   ar_hln;         /* length of hardware address   */
                    585:        unsigned char   ar_pln;         /* length of protocol address   */
                    586:        unsigned short  ar_op;          /* ARP opcode (command)         */
                    587: 
                    588:         /*
                    589:          *      Ethernet looks like this : This bit is variable sized however...
                    590:          */
                    591:        unsigned char           ar_sha[ETH_ALEN];       /* sender hardware address      */
                    592:        unsigned char           ar_sip[4];              /* sender IP address            */
                    593:        unsigned char           ar_tha[ETH_ALEN];       /* target hardware address      */
                    594:        unsigned char           ar_tip[4];              /* target IP address            */
                    595: };
                    596: 
1.1.1.5 ! root      597: static void arp_input(const uint8_t *pkt, int pkt_len)
1.1       root      598: {
                    599:     struct ethhdr *eh = (struct ethhdr *)pkt;
                    600:     struct arphdr *ah = (struct arphdr *)(pkt + ETH_HLEN);
                    601:     uint8_t arp_reply[ETH_HLEN + sizeof(struct arphdr)];
                    602:     struct ethhdr *reh = (struct ethhdr *)arp_reply;
                    603:     struct arphdr *rah = (struct arphdr *)(arp_reply + ETH_HLEN);
                    604:     int ar_op;
                    605:     struct ex_list *ex_ptr;
                    606: 
                    607:     ar_op = ntohs(ah->ar_op);
                    608:     switch(ar_op) {
                    609:     case ARPOP_REQUEST:
                    610:         if (!memcmp(ah->ar_tip, &special_addr, 3)) {
1.1.1.4   root      611:             if (ah->ar_tip[3] == CTL_DNS || ah->ar_tip[3] == CTL_ALIAS)
1.1       root      612:                 goto arp_ok;
                    613:             for (ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {
                    614:                 if (ex_ptr->ex_addr == ah->ar_tip[3])
                    615:                     goto arp_ok;
                    616:             }
                    617:             return;
                    618:         arp_ok:
                    619:             /* XXX: make an ARP request to have the client address */
                    620:             memcpy(client_ethaddr, eh->h_source, ETH_ALEN);
                    621: 
                    622:             /* ARP request for alias/dns mac address */
                    623:             memcpy(reh->h_dest, pkt + ETH_ALEN, ETH_ALEN);
                    624:             memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 1);
                    625:             reh->h_source[5] = ah->ar_tip[3];
                    626:             reh->h_proto = htons(ETH_P_ARP);
                    627: 
                    628:             rah->ar_hrd = htons(1);
                    629:             rah->ar_pro = htons(ETH_P_IP);
                    630:             rah->ar_hln = ETH_ALEN;
                    631:             rah->ar_pln = 4;
                    632:             rah->ar_op = htons(ARPOP_REPLY);
                    633:             memcpy(rah->ar_sha, reh->h_source, ETH_ALEN);
                    634:             memcpy(rah->ar_sip, ah->ar_tip, 4);
                    635:             memcpy(rah->ar_tha, ah->ar_sha, ETH_ALEN);
                    636:             memcpy(rah->ar_tip, ah->ar_sip, 4);
                    637:             slirp_output(arp_reply, sizeof(arp_reply));
                    638:         }
                    639:         break;
1.1.1.5 ! root      640:     case ARPOP_REPLY:
        !           641:         /* reply to request of client mac address ? */
        !           642:         if (!memcmp(client_ethaddr, zero_ethaddr, ETH_ALEN) &&
        !           643:             !memcmp(ah->ar_sip, &client_ipaddr.s_addr, 4)) {
        !           644:             memcpy(client_ethaddr, ah->ar_sha, ETH_ALEN);
        !           645:         }
        !           646:         break;
1.1       root      647:     default:
                    648:         break;
                    649:     }
                    650: }
                    651: 
                    652: void slirp_input(const uint8_t *pkt, int pkt_len)
                    653: {
                    654:     struct mbuf *m;
                    655:     int proto;
                    656: 
                    657:     if (pkt_len < ETH_HLEN)
                    658:         return;
1.1.1.4   root      659: 
1.1       root      660:     proto = ntohs(*(uint16_t *)(pkt + 12));
                    661:     switch(proto) {
                    662:     case ETH_P_ARP:
                    663:         arp_input(pkt, pkt_len);
                    664:         break;
                    665:     case ETH_P_IP:
                    666:         m = m_get();
                    667:         if (!m)
                    668:             return;
1.1.1.3   root      669:         /* Note: we add to align the IP header */
1.1.1.5 ! root      670:         if (M_FREEROOM(m) < pkt_len + 2) {
        !           671:             m_inc(m, pkt_len + 2);
        !           672:         }
1.1.1.3   root      673:         m->m_len = pkt_len + 2;
                    674:         memcpy(m->m_data + 2, pkt, pkt_len);
1.1       root      675: 
1.1.1.3   root      676:         m->m_data += 2 + ETH_HLEN;
                    677:         m->m_len -= 2 + ETH_HLEN;
1.1       root      678: 
                    679:         ip_input(m);
                    680:         break;
                    681:     default:
                    682:         break;
                    683:     }
                    684: }
                    685: 
                    686: /* output the IP packet to the ethernet device */
                    687: void if_encap(const uint8_t *ip_data, int ip_data_len)
                    688: {
                    689:     uint8_t buf[1600];
                    690:     struct ethhdr *eh = (struct ethhdr *)buf;
                    691: 
                    692:     if (ip_data_len + ETH_HLEN > sizeof(buf))
                    693:         return;
1.1.1.5 ! root      694:     
        !           695:     if (!memcmp(client_ethaddr, zero_ethaddr, ETH_ALEN)) {
        !           696:         uint8_t arp_req[ETH_HLEN + sizeof(struct arphdr)];
        !           697:         struct ethhdr *reh = (struct ethhdr *)arp_req;
        !           698:         struct arphdr *rah = (struct arphdr *)(arp_req + ETH_HLEN);
        !           699:         const struct ip *iph = (const struct ip *)ip_data;
        !           700: 
        !           701:         /* If the client addr is not known, there is no point in
        !           702:            sending the packet to it. Normally the sender should have
        !           703:            done an ARP request to get its MAC address. Here we do it
        !           704:            in place of sending the packet and we hope that the sender
        !           705:            will retry sending its packet. */
        !           706:         memset(reh->h_dest, 0xff, ETH_ALEN);
        !           707:         memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 1);
        !           708:         reh->h_source[5] = CTL_ALIAS;
        !           709:         reh->h_proto = htons(ETH_P_ARP);
        !           710:         rah->ar_hrd = htons(1);
        !           711:         rah->ar_pro = htons(ETH_P_IP);
        !           712:         rah->ar_hln = ETH_ALEN;
        !           713:         rah->ar_pln = 4;
        !           714:         rah->ar_op = htons(ARPOP_REQUEST);
        !           715:         /* source hw addr */
        !           716:         memcpy(rah->ar_sha, special_ethaddr, ETH_ALEN - 1);
        !           717:         rah->ar_sha[5] = CTL_ALIAS;
        !           718:         /* source IP */
        !           719:         memcpy(rah->ar_sip, &alias_addr, 4);
        !           720:         /* target hw addr (none) */
        !           721:         memset(rah->ar_tha, 0, ETH_ALEN);
        !           722:         /* target IP */
        !           723:         memcpy(rah->ar_tip, &iph->ip_dst, 4);
        !           724:         client_ipaddr = iph->ip_dst;
        !           725:         slirp_output(arp_req, sizeof(arp_req));
        !           726:     } else {
        !           727:         memcpy(eh->h_dest, client_ethaddr, ETH_ALEN);
        !           728:         memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 1);
        !           729:         /* XXX: not correct */
        !           730:         eh->h_source[5] = CTL_ALIAS;
        !           731:         eh->h_proto = htons(ETH_P_IP);
        !           732:         memcpy(buf + sizeof(struct ethhdr), ip_data, ip_data_len);
        !           733:         slirp_output(buf, ip_data_len + ETH_HLEN);
        !           734:     }
1.1       root      735: }
                    736: 
1.1.1.4   root      737: int slirp_redir(int is_udp, int host_port,
1.1       root      738:                 struct in_addr guest_addr, int guest_port)
                    739: {
                    740:     if (is_udp) {
1.1.1.4   root      741:         if (!udp_listen(htons(host_port), guest_addr.s_addr,
1.1       root      742:                         htons(guest_port), 0))
                    743:             return -1;
                    744:     } else {
1.1.1.4   root      745:         if (!solisten(htons(host_port), guest_addr.s_addr,
1.1       root      746:                       htons(guest_port), 0))
                    747:             return -1;
                    748:     }
                    749:     return 0;
                    750: }
                    751: 
1.1.1.5 ! root      752: int slirp_add_exec(int do_pty, const void *args, int addr_low_byte,
1.1       root      753:                   int guest_port)
                    754: {
1.1.1.4   root      755:     return add_exec(&exec_list, do_pty, (char *)args,
1.1       root      756:                     addr_low_byte, htons(guest_port));
                    757: }
1.1.1.5 ! root      758: 
        !           759: ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags)
        !           760: {
        !           761:        if (so->s == -1 && so->extra) {
        !           762:                qemu_chr_write(so->extra, buf, len);
        !           763:                return len;
        !           764:        }
        !           765: 
        !           766:        return send(so->s, buf, len, flags);
        !           767: }
        !           768: 
        !           769: static struct socket *slirp_find_ctl_socket(int addr_low_byte, int guest_port)
        !           770: {
        !           771:        struct socket *so;
        !           772: 
        !           773:        for (so = tcb.so_next; so != &tcb; so = so->so_next) {
        !           774:                if ((so->so_faddr.s_addr & htonl(0xffffff00)) ==
        !           775:                                special_addr.s_addr
        !           776:                                && (ntohl(so->so_faddr.s_addr) & 0xff) ==
        !           777:                                addr_low_byte
        !           778:                                && htons(so->so_fport) == guest_port)
        !           779:                        return so;
        !           780:        }
        !           781: 
        !           782:        return NULL;
        !           783: }
        !           784: 
        !           785: size_t slirp_socket_can_recv(int addr_low_byte, int guest_port)
        !           786: {
        !           787:        struct iovec iov[2];
        !           788:        struct socket *so;
        !           789: 
        !           790:     if (!link_up)
        !           791:         return 0;
        !           792: 
        !           793:        so = slirp_find_ctl_socket(addr_low_byte, guest_port);
        !           794: 
        !           795:        if (!so || so->so_state & SS_NOFDREF)
        !           796:                return 0;
        !           797: 
        !           798:        if (!CONN_CANFRCV(so) || so->so_snd.sb_cc >= (so->so_snd.sb_datalen/2))
        !           799:                return 0;
        !           800: 
        !           801:        return sopreprbuf(so, iov, NULL);
        !           802: }
        !           803: 
        !           804: void slirp_socket_recv(int addr_low_byte, int guest_port, const uint8_t *buf,
        !           805:         int size)
        !           806: {
        !           807:     int ret;
        !           808:     struct socket *so = slirp_find_ctl_socket(addr_low_byte, guest_port);
        !           809:    
        !           810:     if (!so)
        !           811:         return;
        !           812: 
        !           813:     ret = soreadbuf(so, (const char *)buf, size);
        !           814: 
        !           815:     if (ret > 0)
        !           816:         tcp_output(sototcpcb(so));
        !           817: }
        !           818: 
        !           819: static void slirp_tcp_save(QEMUFile *f, struct tcpcb *tp)
        !           820: {
        !           821:     int i;
        !           822: 
        !           823:     qemu_put_sbe16(f, tp->t_state);
        !           824:     for (i = 0; i < TCPT_NTIMERS; i++)
        !           825:         qemu_put_sbe16(f, tp->t_timer[i]);
        !           826:     qemu_put_sbe16(f, tp->t_rxtshift);
        !           827:     qemu_put_sbe16(f, tp->t_rxtcur);
        !           828:     qemu_put_sbe16(f, tp->t_dupacks);
        !           829:     qemu_put_be16(f, tp->t_maxseg);
        !           830:     qemu_put_sbyte(f, tp->t_force);
        !           831:     qemu_put_be16(f, tp->t_flags);
        !           832:     qemu_put_be32(f, tp->snd_una);
        !           833:     qemu_put_be32(f, tp->snd_nxt);
        !           834:     qemu_put_be32(f, tp->snd_up);
        !           835:     qemu_put_be32(f, tp->snd_wl1);
        !           836:     qemu_put_be32(f, tp->snd_wl2);
        !           837:     qemu_put_be32(f, tp->iss);
        !           838:     qemu_put_be32(f, tp->snd_wnd);
        !           839:     qemu_put_be32(f, tp->rcv_wnd);
        !           840:     qemu_put_be32(f, tp->rcv_nxt);
        !           841:     qemu_put_be32(f, tp->rcv_up);
        !           842:     qemu_put_be32(f, tp->irs);
        !           843:     qemu_put_be32(f, tp->rcv_adv);
        !           844:     qemu_put_be32(f, tp->snd_max);
        !           845:     qemu_put_be32(f, tp->snd_cwnd);
        !           846:     qemu_put_be32(f, tp->snd_ssthresh);
        !           847:     qemu_put_sbe16(f, tp->t_idle);
        !           848:     qemu_put_sbe16(f, tp->t_rtt);
        !           849:     qemu_put_be32(f, tp->t_rtseq);
        !           850:     qemu_put_sbe16(f, tp->t_srtt);
        !           851:     qemu_put_sbe16(f, tp->t_rttvar);
        !           852:     qemu_put_be16(f, tp->t_rttmin);
        !           853:     qemu_put_be32(f, tp->max_sndwnd);
        !           854:     qemu_put_byte(f, tp->t_oobflags);
        !           855:     qemu_put_byte(f, tp->t_iobc);
        !           856:     qemu_put_sbe16(f, tp->t_softerror);
        !           857:     qemu_put_byte(f, tp->snd_scale);
        !           858:     qemu_put_byte(f, tp->rcv_scale);
        !           859:     qemu_put_byte(f, tp->request_r_scale);
        !           860:     qemu_put_byte(f, tp->requested_s_scale);
        !           861:     qemu_put_be32(f, tp->ts_recent);
        !           862:     qemu_put_be32(f, tp->ts_recent_age);
        !           863:     qemu_put_be32(f, tp->last_ack_sent);
        !           864: }
        !           865: 
        !           866: static void slirp_sbuf_save(QEMUFile *f, struct sbuf *sbuf)
        !           867: {
        !           868:     uint32_t off;
        !           869: 
        !           870:     qemu_put_be32(f, sbuf->sb_cc);
        !           871:     qemu_put_be32(f, sbuf->sb_datalen);
        !           872:     off = (uint32_t)(sbuf->sb_wptr - sbuf->sb_data);
        !           873:     qemu_put_sbe32(f, off);
        !           874:     off = (uint32_t)(sbuf->sb_rptr - sbuf->sb_data);
        !           875:     qemu_put_sbe32(f, off);
        !           876:     qemu_put_buffer(f, (unsigned char*)sbuf->sb_data, sbuf->sb_datalen);
        !           877: }
        !           878: 
        !           879: static void slirp_socket_save(QEMUFile *f, struct socket *so)
        !           880: {
        !           881:     qemu_put_be32(f, so->so_urgc);
        !           882:     qemu_put_be32(f, so->so_faddr.s_addr);
        !           883:     qemu_put_be32(f, so->so_laddr.s_addr);
        !           884:     qemu_put_be16(f, so->so_fport);
        !           885:     qemu_put_be16(f, so->so_lport);
        !           886:     qemu_put_byte(f, so->so_iptos);
        !           887:     qemu_put_byte(f, so->so_emu);
        !           888:     qemu_put_byte(f, so->so_type);
        !           889:     qemu_put_be32(f, so->so_state);
        !           890:     slirp_sbuf_save(f, &so->so_rcv);
        !           891:     slirp_sbuf_save(f, &so->so_snd);
        !           892:     slirp_tcp_save(f, so->so_tcpcb);
        !           893: }
        !           894: 
        !           895: static void slirp_state_save(QEMUFile *f, void *opaque)
        !           896: {
        !           897:     struct ex_list *ex_ptr;
        !           898: 
        !           899:     for (ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next)
        !           900:         if (ex_ptr->ex_pty == 3) {
        !           901:             struct socket *so;
        !           902:             so = slirp_find_ctl_socket(ex_ptr->ex_addr, ntohs(ex_ptr->ex_fport));
        !           903:             if (!so)
        !           904:                 continue;
        !           905: 
        !           906:             qemu_put_byte(f, 42);
        !           907:             slirp_socket_save(f, so);
        !           908:         }
        !           909:     qemu_put_byte(f, 0);
        !           910: }
        !           911: 
        !           912: static void slirp_tcp_load(QEMUFile *f, struct tcpcb *tp)
        !           913: {
        !           914:     int i;
        !           915: 
        !           916:     tp->t_state = qemu_get_sbe16(f);
        !           917:     for (i = 0; i < TCPT_NTIMERS; i++)
        !           918:         tp->t_timer[i] = qemu_get_sbe16(f);
        !           919:     tp->t_rxtshift = qemu_get_sbe16(f);
        !           920:     tp->t_rxtcur = qemu_get_sbe16(f);
        !           921:     tp->t_dupacks = qemu_get_sbe16(f);
        !           922:     tp->t_maxseg = qemu_get_be16(f);
        !           923:     tp->t_force = qemu_get_sbyte(f);
        !           924:     tp->t_flags = qemu_get_be16(f);
        !           925:     tp->snd_una = qemu_get_be32(f);
        !           926:     tp->snd_nxt = qemu_get_be32(f);
        !           927:     tp->snd_up = qemu_get_be32(f);
        !           928:     tp->snd_wl1 = qemu_get_be32(f);
        !           929:     tp->snd_wl2 = qemu_get_be32(f);
        !           930:     tp->iss = qemu_get_be32(f);
        !           931:     tp->snd_wnd = qemu_get_be32(f);
        !           932:     tp->rcv_wnd = qemu_get_be32(f);
        !           933:     tp->rcv_nxt = qemu_get_be32(f);
        !           934:     tp->rcv_up = qemu_get_be32(f);
        !           935:     tp->irs = qemu_get_be32(f);
        !           936:     tp->rcv_adv = qemu_get_be32(f);
        !           937:     tp->snd_max = qemu_get_be32(f);
        !           938:     tp->snd_cwnd = qemu_get_be32(f);
        !           939:     tp->snd_ssthresh = qemu_get_be32(f);
        !           940:     tp->t_idle = qemu_get_sbe16(f);
        !           941:     tp->t_rtt = qemu_get_sbe16(f);
        !           942:     tp->t_rtseq = qemu_get_be32(f);
        !           943:     tp->t_srtt = qemu_get_sbe16(f);
        !           944:     tp->t_rttvar = qemu_get_sbe16(f);
        !           945:     tp->t_rttmin = qemu_get_be16(f);
        !           946:     tp->max_sndwnd = qemu_get_be32(f);
        !           947:     tp->t_oobflags = qemu_get_byte(f);
        !           948:     tp->t_iobc = qemu_get_byte(f);
        !           949:     tp->t_softerror = qemu_get_sbe16(f);
        !           950:     tp->snd_scale = qemu_get_byte(f);
        !           951:     tp->rcv_scale = qemu_get_byte(f);
        !           952:     tp->request_r_scale = qemu_get_byte(f);
        !           953:     tp->requested_s_scale = qemu_get_byte(f);
        !           954:     tp->ts_recent = qemu_get_be32(f);
        !           955:     tp->ts_recent_age = qemu_get_be32(f);
        !           956:     tp->last_ack_sent = qemu_get_be32(f);
        !           957:     tcp_template(tp);
        !           958: }
        !           959: 
        !           960: static int slirp_sbuf_load(QEMUFile *f, struct sbuf *sbuf)
        !           961: {
        !           962:     uint32_t off, sb_cc, sb_datalen;
        !           963: 
        !           964:     sb_cc = qemu_get_be32(f);
        !           965:     sb_datalen = qemu_get_be32(f);
        !           966: 
        !           967:     sbreserve(sbuf, sb_datalen);
        !           968: 
        !           969:     if (sbuf->sb_datalen != sb_datalen)
        !           970:         return -ENOMEM;
        !           971: 
        !           972:     sbuf->sb_cc = sb_cc;
        !           973: 
        !           974:     off = qemu_get_sbe32(f);
        !           975:     sbuf->sb_wptr = sbuf->sb_data + off;
        !           976:     off = qemu_get_sbe32(f);
        !           977:     sbuf->sb_rptr = sbuf->sb_data + off;
        !           978:     qemu_get_buffer(f, (unsigned char*)sbuf->sb_data, sbuf->sb_datalen);
        !           979: 
        !           980:     return 0;
        !           981: }
        !           982: 
        !           983: static int slirp_socket_load(QEMUFile *f, struct socket *so)
        !           984: {
        !           985:     if (tcp_attach(so) < 0)
        !           986:         return -ENOMEM;
        !           987: 
        !           988:     so->so_urgc = qemu_get_be32(f);
        !           989:     so->so_faddr.s_addr = qemu_get_be32(f);
        !           990:     so->so_laddr.s_addr = qemu_get_be32(f);
        !           991:     so->so_fport = qemu_get_be16(f);
        !           992:     so->so_lport = qemu_get_be16(f);
        !           993:     so->so_iptos = qemu_get_byte(f);
        !           994:     so->so_emu = qemu_get_byte(f);
        !           995:     so->so_type = qemu_get_byte(f);
        !           996:     so->so_state = qemu_get_be32(f);
        !           997:     if (slirp_sbuf_load(f, &so->so_rcv) < 0)
        !           998:         return -ENOMEM;
        !           999:     if (slirp_sbuf_load(f, &so->so_snd) < 0)
        !          1000:         return -ENOMEM;
        !          1001:     slirp_tcp_load(f, so->so_tcpcb);
        !          1002: 
        !          1003:     return 0;
        !          1004: }
        !          1005: 
        !          1006: static int slirp_state_load(QEMUFile *f, void *opaque, int version_id)
        !          1007: {
        !          1008:     struct ex_list *ex_ptr;
        !          1009:     int r;
        !          1010: 
        !          1011:     while ((r = qemu_get_byte(f))) {
        !          1012:         int ret;
        !          1013:         struct socket *so = socreate();
        !          1014: 
        !          1015:         if (!so)
        !          1016:             return -ENOMEM;
        !          1017: 
        !          1018:         ret = slirp_socket_load(f, so);
        !          1019: 
        !          1020:         if (ret < 0)
        !          1021:             return ret;
        !          1022: 
        !          1023:         if ((so->so_faddr.s_addr & htonl(0xffffff00)) != special_addr.s_addr)
        !          1024:             return -EINVAL;
        !          1025: 
        !          1026:         for (ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next)
        !          1027:             if (ex_ptr->ex_pty == 3 &&
        !          1028:                     (ntohl(so->so_faddr.s_addr) & 0xff) == ex_ptr->ex_addr &&
        !          1029:                     so->so_fport == ex_ptr->ex_fport)
        !          1030:                 break;
        !          1031: 
        !          1032:         if (!ex_ptr)
        !          1033:             return -EINVAL;
        !          1034: 
        !          1035:         so->extra = (void *)ex_ptr->ex_exec;
        !          1036:     }
        !          1037: 
        !          1038:     return 0;
        !          1039: }

unix.superglobalmegacorp.com

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