Annotation of qemu/nbd.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  *  Copyright (C) 2005  Anthony Liguori <anthony@codemonkey.ws>
                      3:  *
                      4:  *  Network Block Device
                      5:  *
                      6:  *  This program is free software; you can redistribute it and/or modify
                      7:  *  it under the terms of the GNU General Public License as published by
                      8:  *  the Free Software Foundation; under version 2 of the License.
                      9:  *
                     10:  *  This program is distributed in the hope that it will be useful,
                     11:  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
                     12:  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     13:  *  GNU General Public License for more details.
                     14:  *
                     15:  *  You should have received a copy of the GNU General Public License
                     16:  *  along with this program; if not, write to the Free Software
                     17:  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA
                     18:  */
                     19: 
                     20: #include "nbd.h"
                     21: 
                     22: #include <errno.h>
                     23: #include <string.h>
                     24: #ifndef _WIN32
                     25: #include <sys/ioctl.h>
                     26: #endif
                     27: #ifdef __sun__
                     28: #include <sys/ioccom.h>
                     29: #endif
                     30: #include <ctype.h>
                     31: #include <inttypes.h>
                     32: 
                     33: #include "qemu_socket.h"
                     34: 
                     35: //#define DEBUG_NBD
                     36: 
                     37: #ifdef DEBUG_NBD
                     38: #define TRACE(msg, ...) do { \
                     39:     LOG(msg, ## __VA_ARGS__); \
                     40: } while(0)
                     41: #else
                     42: #define TRACE(msg, ...) \
                     43:     do { } while (0)
                     44: #endif
                     45: 
                     46: #define LOG(msg, ...) do { \
                     47:     fprintf(stderr, "%s:%s():L%d: " msg "\n", \
                     48:             __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__); \
                     49: } while(0)
                     50: 
                     51: /* This is all part of the "official" NBD API */
                     52: 
                     53: #define NBD_REQUEST_MAGIC       0x25609513
                     54: #define NBD_REPLY_MAGIC         0x67446698
                     55: 
                     56: #define NBD_SET_SOCK            _IO(0xab, 0)
                     57: #define NBD_SET_BLKSIZE         _IO(0xab, 1)
                     58: #define NBD_SET_SIZE            _IO(0xab, 2)
                     59: #define NBD_DO_IT               _IO(0xab, 3)
                     60: #define NBD_CLEAR_SOCK          _IO(0xab, 4)
                     61: #define NBD_CLEAR_QUE           _IO(0xab, 5)
                     62: #define NBD_PRINT_DEBUG                _IO(0xab, 6)
                     63: #define NBD_SET_SIZE_BLOCKS    _IO(0xab, 7)
                     64: #define NBD_DISCONNECT          _IO(0xab, 8)
                     65: 
                     66: /* That's all folks */
                     67: 
                     68: #define read_sync(fd, buffer, size) nbd_wr_sync(fd, buffer, size, true)
                     69: #define write_sync(fd, buffer, size) nbd_wr_sync(fd, buffer, size, false)
                     70: 
                     71: size_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read)
                     72: {
                     73:     size_t offset = 0;
                     74: 
                     75:     while (offset < size) {
                     76:         ssize_t len;
                     77: 
                     78:         if (do_read) {
                     79:             len = recv(fd, buffer + offset, size - offset, 0);
                     80:         } else {
                     81:             len = send(fd, buffer + offset, size - offset, 0);
                     82:         }
                     83: 
                     84:         if (len == -1)
                     85:             errno = socket_error();
                     86: 
                     87:         /* recoverable error */
                     88:         if (len == -1 && (errno == EAGAIN || errno == EINTR)) {
                     89:             continue;
                     90:         }
                     91: 
                     92:         /* eof */
                     93:         if (len == 0) {
                     94:             break;
                     95:         }
                     96: 
                     97:         /* unrecoverable error */
                     98:         if (len == -1) {
                     99:             return 0;
                    100:         }
                    101: 
                    102:         offset += len;
                    103:     }
                    104: 
                    105:     return offset;
                    106: }
                    107: 
                    108: int tcp_socket_outgoing(const char *address, uint16_t port)
                    109: {
                    110:     int s;
                    111:     struct in_addr in;
                    112:     struct sockaddr_in addr;
                    113: 
                    114:     s = socket(PF_INET, SOCK_STREAM, 0);
                    115:     if (s == -1) {
                    116:         return -1;
                    117:     }
                    118: 
                    119:     if (inet_aton(address, &in) == 0) {
                    120:         struct hostent *ent;
                    121: 
                    122:         ent = gethostbyname(address);
                    123:         if (ent == NULL) {
                    124:             goto error;
                    125:         }
                    126: 
                    127:         memcpy(&in, ent->h_addr, sizeof(in));
                    128:     }
                    129: 
                    130:     addr.sin_family = AF_INET;
                    131:     addr.sin_port = htons(port);
                    132:     memcpy(&addr.sin_addr.s_addr, &in, sizeof(in));
                    133: 
                    134:     if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
                    135:         goto error;
                    136:     }
                    137: 
                    138:     return s;
                    139: error:
                    140:     closesocket(s);
                    141:     return -1;
                    142: }
                    143: 
                    144: int tcp_socket_incoming(const char *address, uint16_t port)
                    145: {
                    146:     int s;
                    147:     struct in_addr in;
                    148:     struct sockaddr_in addr;
                    149:     int opt;
                    150: 
                    151:     s = socket(PF_INET, SOCK_STREAM, 0);
                    152:     if (s == -1) {
                    153:         return -1;
                    154:     }
                    155: 
                    156:     if (inet_aton(address, &in) == 0) {
                    157:         struct hostent *ent;
                    158: 
                    159:         ent = gethostbyname(address);
                    160:         if (ent == NULL) {
                    161:             goto error;
                    162:         }
                    163: 
                    164:         memcpy(&in, ent->h_addr, sizeof(in));
                    165:     }
                    166: 
                    167:     addr.sin_family = AF_INET;
                    168:     addr.sin_port = htons(port);
                    169:     memcpy(&addr.sin_addr.s_addr, &in, sizeof(in));
                    170: 
                    171:     opt = 1;
                    172:     if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) {
                    173:         goto error;
                    174:     }
                    175: 
                    176:     if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
                    177:         goto error;
                    178:     }
                    179: 
                    180:     if (listen(s, 128) == -1) {
                    181:         goto error;
                    182:     }
                    183: 
                    184:     return s;
                    185: error:
                    186:     closesocket(s);
                    187:     return -1;
                    188: }
                    189: 
                    190: #ifndef _WIN32
                    191: int unix_socket_incoming(const char *path)
                    192: {
                    193:     int s;
                    194:     struct sockaddr_un addr;
                    195: 
                    196:     s = socket(PF_UNIX, SOCK_STREAM, 0);
                    197:     if (s == -1) {
                    198:         return -1;
                    199:     }
                    200: 
                    201:     memset(&addr, 0, sizeof(addr));
                    202:     addr.sun_family = AF_UNIX;
                    203:     pstrcpy(addr.sun_path, sizeof(addr.sun_path), path);
                    204: 
                    205:     if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
                    206:         goto error;
                    207:     }
                    208: 
                    209:     if (listen(s, 128) == -1) {
                    210:         goto error;
                    211:     }
                    212: 
                    213:     return s;
                    214: error:
                    215:     closesocket(s);
                    216:     return -1;
                    217: }
                    218: 
                    219: int unix_socket_outgoing(const char *path)
                    220: {
                    221:     int s;
                    222:     struct sockaddr_un addr;
                    223: 
                    224:     s = socket(PF_UNIX, SOCK_STREAM, 0);
                    225:     if (s == -1) {
                    226:         return -1;
                    227:     }
                    228: 
                    229:     memset(&addr, 0, sizeof(addr));
                    230:     addr.sun_family = AF_UNIX;
                    231:     pstrcpy(addr.sun_path, sizeof(addr.sun_path), path);
                    232: 
                    233:     if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
                    234:         goto error;
                    235:     }
                    236: 
                    237:     return s;
                    238: error:
                    239:     closesocket(s);
                    240:     return -1;
                    241: }
                    242: #else
                    243: int unix_socket_incoming(const char *path)
                    244: {
                    245:     errno = ENOTSUP;
                    246:     return -1;
                    247: }
                    248: 
                    249: int unix_socket_outgoing(const char *path)
                    250: {
                    251:     errno = ENOTSUP;
                    252:     return -1;
                    253: }
                    254: #endif
                    255: 
                    256: 
                    257: /* Basic flow
                    258: 
                    259:    Server         Client
                    260: 
                    261:    Negotiate
                    262:                   Request
                    263:    Response
                    264:                   Request
                    265:    Response
                    266:                   ...
                    267:    ...
                    268:                   Request (type == 2)
                    269: */
                    270: 
                    271: int nbd_negotiate(int csock, off_t size)
                    272: {
                    273:        char buf[8 + 8 + 8 + 128];
                    274: 
                    275:        /* Negotiate
                    276:           [ 0 ..   7]   passwd   ("NBDMAGIC")
                    277:           [ 8 ..  15]   magic    (0x00420281861253)
                    278:           [16 ..  23]   size
                    279:           [24 .. 151]   reserved (0)
                    280:         */
                    281: 
                    282:        TRACE("Beginning negotiation.");
                    283:        memcpy(buf, "NBDMAGIC", 8);
                    284:        cpu_to_be64w((uint64_t*)(buf + 8), 0x00420281861253LL);
                    285:        cpu_to_be64w((uint64_t*)(buf + 16), size);
                    286:        memset(buf + 24, 0, 128);
                    287: 
                    288:        if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) {
                    289:                LOG("write failed");
                    290:                errno = EINVAL;
                    291:                return -1;
                    292:        }
                    293: 
                    294:        TRACE("Negotation succeeded.");
                    295: 
                    296:        return 0;
                    297: }
                    298: 
                    299: int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize)
                    300: {
                    301:        char buf[8 + 8 + 8 + 128];
                    302:        uint64_t magic;
                    303: 
                    304:        TRACE("Receiving negotation.");
                    305: 
                    306:        if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) {
                    307:                LOG("read failed");
                    308:                errno = EINVAL;
                    309:                return -1;
                    310:        }
                    311: 
                    312:        magic = be64_to_cpup((uint64_t*)(buf + 8));
                    313:        *size = be64_to_cpup((uint64_t*)(buf + 16));
                    314:        *blocksize = 1024;
                    315: 
                    316:        TRACE("Magic is %c%c%c%c%c%c%c%c",
                    317:              qemu_isprint(buf[0]) ? buf[0] : '.',
                    318:              qemu_isprint(buf[1]) ? buf[1] : '.',
                    319:              qemu_isprint(buf[2]) ? buf[2] : '.',
                    320:              qemu_isprint(buf[3]) ? buf[3] : '.',
                    321:              qemu_isprint(buf[4]) ? buf[4] : '.',
                    322:              qemu_isprint(buf[5]) ? buf[5] : '.',
                    323:              qemu_isprint(buf[6]) ? buf[6] : '.',
                    324:              qemu_isprint(buf[7]) ? buf[7] : '.');
                    325:        TRACE("Magic is 0x%" PRIx64, magic);
                    326:        TRACE("Size is %" PRIu64, *size);
                    327: 
                    328:        if (memcmp(buf, "NBDMAGIC", 8) != 0) {
                    329:                LOG("Invalid magic received");
                    330:                errno = EINVAL;
                    331:                return -1;
                    332:        }
                    333: 
                    334:        TRACE("Checking magic");
                    335: 
                    336:        if (magic != 0x00420281861253LL) {
                    337:                LOG("Bad magic received");
                    338:                errno = EINVAL;
                    339:                return -1;
                    340:        }
                    341:         return 0;
                    342: }
                    343: 
                    344: #ifndef _WIN32
                    345: int nbd_init(int fd, int csock, off_t size, size_t blocksize)
                    346: {
                    347:        TRACE("Setting block size to %lu", (unsigned long)blocksize);
                    348: 
                    349:        if (ioctl(fd, NBD_SET_BLKSIZE, blocksize) == -1) {
                    350:                int serrno = errno;
                    351:                LOG("Failed setting NBD block size");
                    352:                errno = serrno;
                    353:                return -1;
                    354:        }
                    355: 
                    356:        TRACE("Setting size to %llu block(s)",
                    357:              (unsigned long long)(size / blocksize));
                    358: 
                    359:        if (ioctl(fd, NBD_SET_SIZE_BLOCKS, size / blocksize) == -1) {
                    360:                int serrno = errno;
                    361:                LOG("Failed setting size (in blocks)");
                    362:                errno = serrno;
                    363:                return -1;
                    364:        }
                    365: 
                    366:        TRACE("Clearing NBD socket");
                    367: 
                    368:        if (ioctl(fd, NBD_CLEAR_SOCK) == -1) {
                    369:                int serrno = errno;
                    370:                LOG("Failed clearing NBD socket");
                    371:                errno = serrno;
                    372:                return -1;
                    373:        }
                    374: 
                    375:        TRACE("Setting NBD socket");
                    376: 
                    377:        if (ioctl(fd, NBD_SET_SOCK, csock) == -1) {
                    378:                int serrno = errno;
                    379:                LOG("Failed to set NBD socket");
                    380:                errno = serrno;
                    381:                return -1;
                    382:        }
                    383: 
                    384:        TRACE("Negotiation ended");
                    385: 
                    386:        return 0;
                    387: }
                    388: 
                    389: int nbd_disconnect(int fd)
                    390: {
                    391:        ioctl(fd, NBD_CLEAR_QUE);
                    392:        ioctl(fd, NBD_DISCONNECT);
                    393:        ioctl(fd, NBD_CLEAR_SOCK);
                    394:        return 0;
                    395: }
                    396: 
                    397: int nbd_client(int fd, int csock)
                    398: {
                    399:        int ret;
                    400:        int serrno;
                    401: 
                    402:        TRACE("Doing NBD loop");
                    403: 
                    404:        ret = ioctl(fd, NBD_DO_IT);
                    405:        serrno = errno;
                    406: 
                    407:        TRACE("NBD loop returned %d: %s", ret, strerror(serrno));
                    408: 
                    409:        TRACE("Clearing NBD queue");
                    410:        ioctl(fd, NBD_CLEAR_QUE);
                    411: 
                    412:        TRACE("Clearing NBD socket");
                    413:        ioctl(fd, NBD_CLEAR_SOCK);
                    414: 
                    415:        errno = serrno;
                    416:        return ret;
                    417: }
                    418: #else
                    419: int nbd_init(int fd, int csock, off_t size, size_t blocksize)
                    420: {
                    421:     errno = ENOTSUP;
                    422:     return -1;
                    423: }
                    424: 
                    425: int nbd_disconnect(int fd)
                    426: {
                    427:     errno = ENOTSUP;
                    428:     return -1;
                    429: }
                    430: 
                    431: int nbd_client(int fd, int csock)
                    432: {
                    433:     errno = ENOTSUP;
                    434:     return -1;
                    435: }
                    436: #endif
                    437: 
                    438: int nbd_send_request(int csock, struct nbd_request *request)
                    439: {
                    440:        uint8_t buf[4 + 4 + 8 + 8 + 4];
                    441: 
                    442:        cpu_to_be32w((uint32_t*)buf, NBD_REQUEST_MAGIC);
                    443:        cpu_to_be32w((uint32_t*)(buf + 4), request->type);
                    444:        cpu_to_be64w((uint64_t*)(buf + 8), request->handle);
                    445:        cpu_to_be64w((uint64_t*)(buf + 16), request->from);
                    446:        cpu_to_be32w((uint32_t*)(buf + 24), request->len);
                    447: 
                    448:        TRACE("Sending request to client");
                    449: 
                    450:        if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) {
                    451:                LOG("writing to socket failed");
                    452:                errno = EINVAL;
                    453:                return -1;
                    454:        }
                    455:        return 0;
                    456: }
                    457: 
                    458: 
                    459: static int nbd_receive_request(int csock, struct nbd_request *request)
                    460: {
                    461:        uint8_t buf[4 + 4 + 8 + 8 + 4];
                    462:        uint32_t magic;
                    463: 
                    464:        if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) {
                    465:                LOG("read failed");
                    466:                errno = EINVAL;
                    467:                return -1;
                    468:        }
                    469: 
                    470:        /* Request
                    471:           [ 0 ..  3]   magic   (NBD_REQUEST_MAGIC)
                    472:           [ 4 ..  7]   type    (0 == READ, 1 == WRITE)
                    473:           [ 8 .. 15]   handle
                    474:           [16 .. 23]   from
                    475:           [24 .. 27]   len
                    476:         */
                    477: 
                    478:        magic = be32_to_cpup((uint32_t*)buf);
                    479:        request->type  = be32_to_cpup((uint32_t*)(buf + 4));
                    480:        request->handle = be64_to_cpup((uint64_t*)(buf + 8));
                    481:        request->from  = be64_to_cpup((uint64_t*)(buf + 16));
                    482:        request->len   = be32_to_cpup((uint32_t*)(buf + 24));
                    483: 
                    484:        TRACE("Got request: "
                    485:              "{ magic = 0x%x, .type = %d, from = %" PRIu64" , len = %u }",
                    486:              magic, request->type, request->from, request->len);
                    487: 
                    488:        if (magic != NBD_REQUEST_MAGIC) {
                    489:                LOG("invalid magic (got 0x%x)", magic);
                    490:                errno = EINVAL;
                    491:                return -1;
                    492:        }
                    493:        return 0;
                    494: }
                    495: 
                    496: int nbd_receive_reply(int csock, struct nbd_reply *reply)
                    497: {
                    498:        uint8_t buf[4 + 4 + 8];
                    499:        uint32_t magic;
                    500: 
                    501:        memset(buf, 0xAA, sizeof(buf));
                    502: 
                    503:        if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) {
                    504:                LOG("read failed");
                    505:                errno = EINVAL;
                    506:                return -1;
                    507:        }
                    508: 
                    509:        /* Reply
                    510:           [ 0 ..  3]    magic   (NBD_REPLY_MAGIC)
                    511:           [ 4 ..  7]    error   (0 == no error)
                    512:           [ 7 .. 15]    handle
                    513:         */
                    514: 
                    515:        magic = be32_to_cpup((uint32_t*)buf);
                    516:        reply->error  = be32_to_cpup((uint32_t*)(buf + 4));
                    517:        reply->handle = be64_to_cpup((uint64_t*)(buf + 8));
                    518: 
                    519:        TRACE("Got reply: "
                    520:              "{ magic = 0x%x, .error = %d, handle = %" PRIu64" }",
                    521:              magic, reply->error, reply->handle);
                    522: 
                    523:        if (magic != NBD_REPLY_MAGIC) {
                    524:                LOG("invalid magic (got 0x%x)", magic);
                    525:                errno = EINVAL;
                    526:                return -1;
                    527:        }
                    528:        return 0;
                    529: }
                    530: 
                    531: static int nbd_send_reply(int csock, struct nbd_reply *reply)
                    532: {
                    533:        uint8_t buf[4 + 4 + 8];
                    534: 
                    535:        /* Reply
                    536:           [ 0 ..  3]    magic   (NBD_REPLY_MAGIC)
                    537:           [ 4 ..  7]    error   (0 == no error)
                    538:           [ 7 .. 15]    handle
                    539:         */
                    540:        cpu_to_be32w((uint32_t*)buf, NBD_REPLY_MAGIC);
                    541:        cpu_to_be32w((uint32_t*)(buf + 4), reply->error);
                    542:        cpu_to_be64w((uint64_t*)(buf + 8), reply->handle);
                    543: 
                    544:        TRACE("Sending response to client");
                    545: 
                    546:        if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) {
                    547:                LOG("writing to socket failed");
                    548:                errno = EINVAL;
                    549:                return -1;
                    550:        }
                    551:        return 0;
                    552: }
                    553: 
                    554: int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset,
                    555:              off_t *offset, bool readonly, uint8_t *data, int data_size)
                    556: {
                    557:        struct nbd_request request;
                    558:        struct nbd_reply reply;
                    559: 
                    560:        TRACE("Reading request.");
                    561: 
                    562:        if (nbd_receive_request(csock, &request) == -1)
                    563:                return -1;
                    564: 
                    565:        if (request.len > data_size) {
                    566:                LOG("len (%u) is larger than max len (%u)",
                    567:                    request.len, data_size);
                    568:                errno = EINVAL;
                    569:                return -1;
                    570:        }
                    571: 
                    572:        if ((request.from + request.len) < request.from) {
                    573:                LOG("integer overflow detected! "
                    574:                    "you're probably being attacked");
                    575:                errno = EINVAL;
                    576:                return -1;
                    577:        }
                    578: 
                    579:        if ((request.from + request.len) > size) {
                    580:                LOG("From: %" PRIu64 ", Len: %u, Size: %" PRIu64
                    581:                    ", Offset: %" PRIu64 "\n",
                    582:                     request.from, request.len, size, dev_offset);
                    583:                LOG("requested operation past EOF--bad client?");
                    584:                errno = EINVAL;
                    585:                return -1;
                    586:        }
                    587: 
                    588:        TRACE("Decoding type");
                    589: 
                    590:        reply.handle = request.handle;
                    591:        reply.error = 0;
                    592: 
                    593:        switch (request.type) {
                    594:        case NBD_CMD_READ:
                    595:                TRACE("Request type is READ");
                    596: 
                    597:                if (bdrv_read(bs, (request.from + dev_offset) / 512, data,
                    598:                              request.len / 512) == -1) {
                    599:                        LOG("reading from file failed");
                    600:                        errno = EINVAL;
                    601:                        return -1;
                    602:                }
                    603:                *offset += request.len;
                    604: 
                    605:                TRACE("Read %u byte(s)", request.len);
                    606: 
                    607:                if (nbd_send_reply(csock, &reply) == -1)
                    608:                        return -1;
                    609: 
                    610:                TRACE("Sending data to client");
                    611: 
                    612:                if (write_sync(csock, data, request.len) != request.len) {
                    613:                        LOG("writing to socket failed");
                    614:                        errno = EINVAL;
                    615:                        return -1;
                    616:                }
                    617:                break;
                    618:        case NBD_CMD_WRITE:
                    619:                TRACE("Request type is WRITE");
                    620: 
                    621:                TRACE("Reading %u byte(s)", request.len);
                    622: 
                    623:                if (read_sync(csock, data, request.len) != request.len) {
                    624:                        LOG("reading from socket failed");
                    625:                        errno = EINVAL;
                    626:                        return -1;
                    627:                }
                    628: 
                    629:                if (readonly) {
                    630:                        TRACE("Server is read-only, return error");
                    631:                        reply.error = 1;
                    632:                } else {
                    633:                        TRACE("Writing to device");
                    634: 
                    635:                        if (bdrv_write(bs, (request.from + dev_offset) / 512,
                    636:                                       data, request.len / 512) == -1) {
                    637:                                LOG("writing to file failed");
                    638:                                errno = EINVAL;
                    639:                                return -1;
                    640:                        }
                    641: 
                    642:                        *offset += request.len;
                    643:                }
                    644: 
                    645:                if (nbd_send_reply(csock, &reply) == -1)
                    646:                        return -1;
                    647:                break;
                    648:        case NBD_CMD_DISC:
                    649:                TRACE("Request type is DISCONNECT");
                    650:                errno = 0;
                    651:                return 1;
                    652:        default:
                    653:                LOG("invalid request type (%u) received", request.type);
                    654:                errno = EINVAL;
                    655:                return -1;
                    656:        }
                    657: 
                    658:        TRACE("Request/Reply complete");
                    659: 
                    660:        return 0;
                    661: }

unix.superglobalmegacorp.com