socket.c
上传用户:xxcykj
上传日期:2007-01-04
资源大小:727k
文件大小:17k
源码类别:

Email客户端

开发平台:

Unix_Linux

  1. /*
  2.  * socket.c -- socket library functions
  3.  *
  4.  * Copyright 1998 by Eric S. Raymond.
  5.  * For license terms, see the file COPYING in this directory.
  6.  */
  7. #include "config.h"
  8. #include <stdio.h>
  9. #include <errno.h>
  10. #include <string.h>
  11. #ifdef HAVE_MEMORY_H
  12. #include <memory.h>
  13. #endif /* HAVE_MEMORY_H */
  14. #include <sys/types.h>
  15. #include <sys/socket.h>
  16. #include <netinet/in.h>
  17. #include <arpa/inet.h>
  18. #include <netdb.h>
  19. #if defined(STDC_HEADERS)
  20. #include <stdlib.h>
  21. #endif
  22. #if defined(HAVE_UNISTD_H)
  23. #include <unistd.h>
  24. #endif
  25. #if defined(HAVE_STDARG_H)
  26. #include <stdarg.h>
  27. #else
  28. #include <varargs.h>
  29. #endif
  30. #include "socket.h"
  31. #include "fetchmail.h"
  32. #include "i18n.h"
  33. /* We need to define h_errno only if it is not already */
  34. #ifndef h_errno
  35. #ifdef HAVE_RES_SEARCH
  36. /* some versions of FreeBSD should declare this but don't */
  37. extern int h_errno;
  38. #else
  39. /* pretend we have h_errno to avoid some #ifdef's later */
  40. static int h_errno;
  41. #endif
  42. #endif /* ndef h_errno */
  43. #if NET_SECURITY
  44. #include <net/security.h>
  45. #endif /* NET_SECURITY */
  46. #ifdef HAVE_SOCKETPAIR
  47. static int handle_plugin(const char *host,
  48.  const char *service, const char *plugin)
  49. /* get a socket mediated through a given external command */
  50. {
  51.     int fds[2];
  52.     if (socketpair(AF_UNIX,SOCK_STREAM,0,fds))
  53.     {
  54. report(stderr, _("fetchmail: socketpair failedn"));
  55. return -1;
  56.     }
  57.     switch (fork()) {
  58. case -1:
  59. /* error */
  60. report(stderr, _("fetchmail: fork failedn"));
  61. return -1;
  62. break;
  63. case 0: /* child */
  64. /* fds[1] is the parent's end; close it for proper EOF
  65. ** detection */
  66. (void) close(fds[1]);
  67. if ( (dup2(fds[0],0) == -1) || (dup2(fds[0],1) == -1) ) {
  68. report(stderr, _("dup2 failedn"));
  69. exit(1);
  70. }
  71. /* fds[0] is now connected to 0 and 1; close it */
  72. (void) close(fds[0]);
  73. if (outlevel >= O_VERBOSE)
  74.     report(stderr, _("running %s %s %sn"), plugin, host, service);
  75. execlp(plugin,plugin,host,service,0);
  76. report(stderr, _("execl(%s) failedn"), plugin);
  77. exit(0);
  78. break;
  79. default: /* parent */
  80. /* NOP */
  81. break;
  82.     }
  83.     /* fds[0] is the child's end; close it for proper EOF detection */
  84.     (void) close(fds[0]);
  85.     return fds[1];
  86. }
  87. #endif /* HAVE_SOCKETPAIR */
  88. #ifdef __UNUSED__
  89. #include <sys/time.h>
  90. int SockCheckOpen(int fd)
  91. /* poll given socket; is it selectable? */
  92. {
  93.     fd_set r, w, e;
  94.     int rt;
  95.     struct timeval tv;
  96.   
  97.     for (;;) 
  98.     {
  99. FD_ZERO(&r); FD_ZERO(&w); FD_ZERO(&e);
  100. FD_SET(fd, &e);
  101.     
  102. tv.tv_sec = 0; tv.tv_usec = 0;
  103. rt = select(fd+1, &r, &w, &e, &tv);
  104. if (rt == -1 && (errno != EAGAIN && errno != EINTR))
  105.     return 0;
  106. if (rt != -1)
  107.     return 1;
  108.     }
  109. }
  110. #endif /* __UNUSED__ */
  111. #if INET6_ENABLE
  112. int SockOpen(const char *host, const char *service, const char *options,
  113.      const char *plugin)
  114. {
  115.     struct addrinfo *ai, req;
  116.     int i;
  117. #if NET_SECURITY
  118.     void *request = NULL;
  119.     int requestlen;
  120. #endif /* NET_SECURITY */
  121. #ifdef HAVE_SOCKETPAIR
  122.     if (plugin)
  123. return handle_plugin(host,service,plugin);
  124. #endif /* HAVE_SOCKETPAIR */
  125.     memset(&req, 0, sizeof(struct addrinfo));
  126.     req.ai_socktype = SOCK_STREAM;
  127.     if (getaddrinfo(host, service, &req, &ai)) {
  128. report(stderr, _("fetchmail: getaddrinfo(%s.%s)n"), host,service);
  129. return -1;
  130.     };
  131. #if NET_SECURITY
  132.     if (!options)
  133. requestlen = 0;
  134.     else
  135. if (net_security_strtorequest((char *)options, &request, &requestlen))
  136.     goto ret;
  137.     i = inner_connect(ai, request, requestlen, NULL, NULL, "fetchmail", NULL);
  138.     if (request)
  139. free(request);
  140.  ret:
  141. #else /* NET_SECURITY */
  142. #ifdef HAVE_INNER_CONNECT
  143.     i = inner_connect(ai, NULL, 0, NULL, NULL, "fetchmail", NULL);
  144. #else
  145.     i = socket(ai->ai_family, ai->ai_socktype, 0);
  146.     if (i < 0) {
  147. freeaddrinfo(ai);
  148. return -1;
  149.     }
  150.     if (connect(i, (struct sockaddr *) ai->ai_addr, ai->ai_addrlen) < 0) {
  151. freeaddrinfo(ai);
  152. SockClose(i);
  153. return -1;
  154.     }
  155. #endif
  156. #endif /* NET_SECURITY */
  157.     freeaddrinfo(ai);
  158.     return i;
  159. };
  160. #else /* INET6_ENABLE */
  161. #ifndef HAVE_INET_ATON
  162. #ifndef  INADDR_NONE
  163. #ifdef   INADDR_BROADCAST
  164. #define  INADDR_NONE INADDR_BROADCAST
  165. #else
  166. #define  INADDR_NONE -1
  167. #endif
  168. #endif
  169. #endif /* HAVE_INET_ATON */
  170. int SockOpen(const char *host, int clientPort, const char *options,
  171.      const char *plugin)
  172. {
  173.     int sock = -1; /* pacify -Wall */
  174. #ifndef HAVE_INET_ATON
  175.     unsigned long inaddr;
  176. #endif /* HAVE_INET_ATON */
  177.     struct sockaddr_in ad, **pptr;
  178.     struct hostent *hp;
  179. #ifdef HAVE_SOCKETPAIR
  180.     if (plugin) {
  181.       char buf[10];
  182.       sprintf(buf,"%d",clientPort);
  183.       return handle_plugin(host,buf,plugin);
  184.     }
  185. #endif /* HAVE_SOCKETPAIR */
  186.     memset(&ad, 0, sizeof(ad));
  187.     ad.sin_family = AF_INET;
  188.     /* we'll accept a quad address */
  189. #ifndef HAVE_INET_ATON
  190.     inaddr = inet_addr(host);
  191.     if (inaddr != INADDR_NONE)
  192.     {
  193.         memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));
  194. #else
  195.     if (inet_aton(host, &ad.sin_addr))
  196.     {
  197. #endif /* HAVE_INET_ATON */
  198.         ad.sin_port = htons(clientPort);
  199.         sock = socket(AF_INET, SOCK_STREAM, 0);
  200.         if (sock < 0)
  201.         {
  202.             h_errno = 0;
  203.             return -1;
  204.         }
  205.         if (connect(sock, (struct sockaddr *) &ad, sizeof(ad)) < 0)
  206.         {
  207.             int olderr = errno;
  208.             SockClose(sock);
  209.             h_errno = 0;
  210.             errno = olderr;
  211.             return -1;
  212.         }
  213. #ifndef HAVE_INET_ATON
  214.     }
  215. #else
  216.     }
  217. #endif /* HAVE_INET_ATON */
  218.     else {
  219.         hp = gethostbyname(host);
  220.         if (hp == NULL)
  221. {
  222.     errno = 0;
  223.     return -1;
  224. }
  225. /*
  226.  * Add a check to make sure the address has a valid IPv4 or IPv6
  227.  * length.  This prevents buffer spamming by a broken DNS.
  228.  */
  229. if(hp->h_length != 4 && hp->h_length != 8)
  230. {
  231.     h_errno = errno = 0;
  232.     report(stderr, 
  233.    _("fetchmail: illegal address length received for host %sn"),host);
  234.     return -1;
  235. }
  236. /*
  237.  * Try all addresses of a possibly multihomed host until we get
  238.  * a successful connect or until we run out of addresses.
  239.  */
  240. pptr = (struct sockaddr_in **)hp->h_addr_list;
  241. for(; *pptr != NULL; pptr++)
  242. {
  243.     sock = socket(AF_INET, SOCK_STREAM, 0);
  244.     if (sock < 0)
  245.     {
  246. h_errno = 0;
  247. return -1;
  248.     }
  249.     ad.sin_port = htons(clientPort);
  250.     memcpy(&ad.sin_addr, *pptr, sizeof(struct in_addr));
  251.     if (connect(sock, (struct sockaddr *) &ad, sizeof(ad)) == 0)
  252. break; /* success */
  253.     SockClose(sock);
  254.     memset(&ad, 0, sizeof(ad));
  255.     ad.sin_family = AF_INET;
  256. }
  257. if(*pptr == NULL)
  258. {
  259.     int olderr = errno;
  260.     SockClose(sock);
  261.     h_errno = 0;
  262.     errno = olderr;
  263.     return -1;
  264. }
  265.     }
  266.     return(sock);
  267. }
  268. #endif /* INET6_ENABLE */
  269. #if defined(HAVE_STDARG_H)
  270. int SockPrintf(int sock, const char* format, ...)
  271. {
  272. #else
  273. int SockPrintf(sock,format,va_alist)
  274. int sock;
  275. char *format;
  276. va_dcl {
  277. #endif
  278.     va_list ap;
  279.     char buf[8192];
  280. #if defined(HAVE_STDARG_H)
  281.     va_start(ap, format) ;
  282. #else
  283.     va_start(ap);
  284. #endif
  285. #ifdef HAVE_VSNPRINTF
  286.     vsnprintf(buf, sizeof(buf), format, ap);
  287. #else
  288.     vsprintf(buf, format, ap);
  289. #endif
  290.     va_end(ap);
  291.     return SockWrite(sock, buf, strlen(buf));
  292. }
  293. #ifdef SSL_ENABLE
  294. #include "ssl.h"
  295. #include "err.h"
  296. #include "pem.h"
  297. #include "x509.h"
  298. static SSL_CTX *_ctx = NULL;
  299. static SSL *_ssl_context[FD_SETSIZE];
  300. SSL *SSLGetContext( int );
  301. #endif /* SSL_ENABLE */
  302. int SockWrite(int sock, char *buf, int len)
  303. {
  304.     int n, wrlen = 0;
  305. #ifdef SSL_ENABLE
  306.     SSL *ssl;
  307. #endif
  308.     while (len)
  309.     {
  310. #ifdef SSL_ENABLE
  311. if( NULL != ( ssl = SSLGetContext( sock ) ) )
  312. n = SSL_write(ssl, buf, len);
  313. else
  314.          n = write(sock, buf, len);
  315. #else
  316.         n = write(sock, buf, len);
  317. #endif
  318.         if (n <= 0)
  319.             return -1;
  320.         len -= n;
  321. wrlen += n;
  322. buf += n;
  323.     }
  324.     return wrlen;
  325. }
  326. int SockRead(int sock, char *buf, int len)
  327. {
  328.     char *newline, *bp = buf;
  329.     int n;
  330. #ifdef SSL_ENABLE
  331.     SSL *ssl;
  332. #endif
  333.     if (--len < 1)
  334. return(-1);
  335.     do {
  336. /* 
  337.  * The reason for these gymnastics is that we want two things:
  338.  * (1) to read n-terminated lines,
  339.  * (2) to return the true length of data read, even if the
  340.  *     data coming in has embedded NULS.
  341.  */
  342. #ifdef SSL_ENABLE
  343. if( NULL != ( ssl = SSLGetContext( sock ) ) ) {
  344. /* Hack alert! */
  345. /* OK...  SSL_peek works a little different from MSG_PEEK
  346. Problem is that SSL_peek can return 0 if there
  347. is no data currently available.  If, on the other
  348. hand, we loose the socket, we also get a zero, but
  349. the SSL_read then SEGFAULTS!  To deal with this,
  350. we'll check the error code any time we get a return
  351. of zero from SSL_peek.  If we have an error, we bail.
  352. If we don't, we read one character in SSL_read and
  353. loop.  This should continue to work even if they
  354. later change the behavior of SSL_peek
  355. to "fix" this problem...  :-( */
  356. if ((n = SSL_peek(ssl, bp, len)) < 0) {
  357. return(-1);
  358. }
  359. if( 0 == n ) {
  360. /* SSL_peek says no data...  Does he mean no data
  361. or did the connection blow up?  If we got an error
  362. then bail! */
  363. if( 0 != ( n = ERR_get_error() ) ) {
  364. return -1;
  365. }
  366. /* We didn't get an error so read at least one
  367. character at this point and loop */
  368. n = 1;
  369. /* Make sure newline start out NULL!
  370.  * We don't have a string to pass through
  371.  * the strchr at this point yet */
  372. newline = NULL;
  373. } else if ((newline = memchr(bp, 'n', n)) != NULL)
  374. n = newline - bp + 1;
  375. if ((n = SSL_read(ssl, bp, n)) == -1) {
  376. return(-1);
  377. }
  378. /* Check for case where our single character turned out to
  379.  * be a newline...  (It wasn't going to get caught by
  380.  * the strchr above if it came from the hack...  ). */
  381. if( NULL == newline && 1 == n && 'n' == *bp ) {
  382. /* Got our newline - this will break
  383. out of the loop now */
  384. newline = bp;
  385. }
  386. } else {
  387. if ((n = recv(sock, bp, len, MSG_PEEK)) <= 0)
  388. return(-1);
  389. if ((newline = memchr(bp, 'n', n)) != NULL)
  390. n = newline - bp + 1;
  391. if ((n = read(sock, bp, n)) == -1)
  392. return(-1);
  393. }
  394. #else
  395. if ((n = recv(sock, bp, len, MSG_PEEK)) <= 0)
  396.     return(-1);
  397. if ((newline = memchr(bp, 'n', n)) != NULL)
  398.     n = newline - bp + 1;
  399. if ((n = read(sock, bp, n)) == -1)
  400.     return(-1);
  401. #endif
  402. bp += n;
  403. len -= n;
  404.     } while 
  405.     (!newline && len);
  406.     *bp = '';
  407.     return bp - buf;
  408. }
  409. int SockPeek(int sock)
  410. /* peek at the next socket character without actually reading it */
  411. {
  412.     int n;
  413.     char ch;
  414. #ifdef SSL_ENABLE
  415.     SSL *ssl;
  416. #endif
  417. #ifdef SSL_ENABLE
  418. if( NULL != ( ssl = SSLGetContext( sock ) ) ) {
  419. n = SSL_peek(ssl, &ch, 1);
  420. if( 0 == n ) {
  421. /* This code really needs to implement a "hold back"
  422.  * to simulate a functioning SSL_peek()...  sigh...
  423.  * Has to be coordinated with the read code above.
  424.  * Next on the list todo... */
  425. /* SSL_peek says no data...  Does he mean no data
  426. or did the connection blow up?  If we got an error
  427. then bail! */
  428. if( 0 != ( n = ERR_get_error() ) ) {
  429. return -1;
  430. }
  431. /* Haven't seen this case actually occur, but...
  432.    if the problem in SockRead can occur, this should
  433.    be possible...  Just not sure what to do here.
  434.    This should be a safe "punt" the "peek" but don't
  435.    "punt" the "session"... */
  436. return 0; /* Give him a '' character */
  437. }
  438. } else {
  439.      n = recv(sock, &ch, 1, MSG_PEEK);
  440. }
  441. #else
  442.      n = recv(sock, &ch, 1, MSG_PEEK);
  443. #endif
  444. if (n == -1)
  445. return -1;
  446. else
  447. return(ch);
  448. }
  449. #ifdef SSL_ENABLE
  450. static char *_ssl_server_cname = NULL;
  451. SSL *SSLGetContext( int sock )
  452. {
  453. /* If SSLOpen has never initialized - just return NULL */
  454. if( NULL == _ctx )
  455. return NULL;
  456. if( sock < 0 || sock > FD_SETSIZE )
  457. return NULL;
  458. return _ssl_context[sock];
  459. }
  460. int SSL_verify_callback( int ok_return, X509_STORE_CTX *ctx )
  461. {
  462. char buf[260];
  463. char cbuf[260];
  464. char ibuf[260];
  465. char *str_ptr;
  466. X509 *x509_cert;
  467. int err, depth;
  468. x509_cert = X509_STORE_CTX_get_current_cert(ctx);
  469. err = X509_STORE_CTX_get_error(ctx);
  470. depth = X509_STORE_CTX_get_error_depth(ctx);
  471. X509_NAME_oneline(X509_get_subject_name(x509_cert), buf, 256);
  472. X509_NAME_oneline(X509_get_issuer_name(x509_cert), ibuf, 256);
  473. /* Just to be sure those buffers are terminated...  I think the
  474. X509 libraries do, but... */
  475. buf[256] = ibuf[256] = '';
  476. if (depth == 0) {
  477. if( ( str_ptr = strstr( ibuf, "/O=" ) ) ) {
  478. str_ptr += 3;
  479. strcpy( cbuf, str_ptr );
  480. if( ( str_ptr = strchr(cbuf, '/' ) ) ) {
  481. *str_ptr = '';
  482. }
  483. if (outlevel == O_VERBOSE)
  484. report(stdout, "Issuer Organization: %sn", cbuf );
  485. } else {
  486. if (outlevel == O_VERBOSE)
  487. report(stdout, "Unknown Organizationn", cbuf );
  488. }
  489. if( ( str_ptr = strstr( ibuf, "/CN=" ) ) ) {
  490. str_ptr += 4;
  491. strcpy( cbuf, str_ptr );
  492. if( ( str_ptr = strchr(cbuf, '/' ) ) ) {
  493. *str_ptr = '';
  494. }
  495. if (outlevel == O_VERBOSE)
  496. report(stdout, "Issuer CommonName: %sn", cbuf );
  497. } else {
  498. if (outlevel == O_VERBOSE)
  499. report(stdout, "Unknown Issuer CommonNamen", cbuf );
  500. }
  501. if( ( str_ptr = strstr( buf, "/CN=" ) ) ) {
  502. str_ptr += 4;
  503. strcpy( cbuf, str_ptr );
  504. if( ( str_ptr = strchr(cbuf, '/' ) ) ) {
  505. *str_ptr = '';
  506. }
  507. if (outlevel == O_VERBOSE)
  508. report(stdout, "Server CommonName: %sn", cbuf );
  509. /* Should we have some wildcarding here? */
  510. if ( NULL != _ssl_server_cname
  511.      && 0 != strcmp( cbuf, _ssl_server_cname ) ) {
  512. report(stdout,
  513.        "Server CommonName mismatch: %s != %sn",
  514.        cbuf, _ssl_server_cname );
  515. }
  516. } else {
  517. if (outlevel == O_VERBOSE)
  518. report(stdout, "Unknown Server CommonNamen", cbuf );
  519. }
  520. }
  521. switch (ctx->error) {
  522. case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
  523. X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256);
  524. report(stdout, "unknown issuer= %s", buf);
  525. break;
  526. case X509_V_ERR_CERT_NOT_YET_VALID:
  527. case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
  528. report(stderr, "Server Certificate not yet valid");
  529. break;
  530. case X509_V_ERR_CERT_HAS_EXPIRED:
  531. case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
  532. report(stderr, "Server Certificate expired");
  533. break;
  534. }
  535. /* We are not requiring or validating server or issuer id's as yet */
  536. /* Always return OK from here */
  537. ok_return = 1;
  538. return( ok_return );
  539. }
  540. /* performs initial SSL handshake over the connected socket
  541.  * uses SSL *ssl global variable, which is currently defined
  542.  * in this file
  543.  */
  544. int SSLOpen(int sock, char *mycert, char *mykey, char *servercname )
  545. {
  546. SSL_load_error_strings();
  547. SSLeay_add_ssl_algorithms();
  548. if( sock < 0 || sock > FD_SETSIZE ) {
  549. report(stderr, "File descriptor out of range for SSL" );
  550. return( -1 );
  551. }
  552. if( ! _ctx ) {
  553. /* Be picky and make sure the memory is cleared */
  554. memset( _ssl_context, 0, sizeof( _ssl_context ) );
  555. _ctx = SSL_CTX_new(SSLv23_client_method());
  556. if(_ctx == NULL) {
  557. ERR_print_errors_fp(stderr);
  558. return(-1);
  559. }
  560. }
  561. _ssl_context[sock] = SSL_new(_ctx);
  562. if(_ssl_context[sock] == NULL) {
  563. ERR_print_errors_fp(stderr);
  564. return(-1);
  565. }
  566. /* This static is for the verify callback */
  567. _ssl_server_cname = servercname;
  568.         SSL_CTX_set_verify(_ctx, SSL_VERIFY_PEER, SSL_verify_callback);
  569. if( mycert || mykey ) {
  570. /* Ok...  He has a certificate file defined, so lets declare it.  If
  571.  * he does NOT have a separate certificate and private key file then
  572.  * assume that it's a combined key and certificate file.
  573.  */
  574. if( !mykey )
  575. mykey = mycert;
  576. if( !mycert )
  577. mycert = mykey;
  578.          SSL_use_certificate_file(_ssl_context[sock], mycert, SSL_FILETYPE_PEM);
  579.          SSL_use_RSAPrivateKey_file(_ssl_context[sock], mykey, SSL_FILETYPE_PEM);
  580. }
  581. SSL_set_fd(_ssl_context[sock], sock);
  582. if(SSL_connect(_ssl_context[sock]) == -1) {
  583. ERR_print_errors_fp(stderr);
  584. return(-1);
  585. }
  586. return(0);
  587. }
  588. #endif
  589. int SockClose(int sock)
  590. /* close a socket gracefully */
  591. {
  592.     char ch;
  593. #ifdef SSL_ENABLE
  594.     SSL *ssl;
  595.     if( NULL != ( ssl = SSLGetContext( sock ) ) ) {
  596.         /* Clean up the SSL stack */
  597.         SSL_free( _ssl_context[sock] );
  598.         _ssl_context[sock] = NULL;
  599.     }
  600. #endif
  601.     /* Half-close the connection first so the other end gets notified.
  602.      *
  603.      * This stops sends but allows receives (effectively, it sends a
  604.      * TCP <FIN>).  We ignore the return from this function because
  605.      * some older BSD-based implementations fail shutdown() if a TCP
  606.      * reset has been recieved.  In any case, if it fails it means the
  607.      * connection is already closed anyway, so it doesn't matter.
  608.      */
  609.     shutdown(sock, 1);
  610.     /* If there is any data still waiting in the queue, discard it.
  611.      * Call recv() until either it returns 0 (meaning we received a FIN)
  612.      * or any error occurs.  This makes sure all data sent by the other
  613.      * side is acknowledged at the TCP level.
  614.      */
  615.     if (recv(sock, &ch, 1, MSG_PEEK) > 0)
  616. while (read(sock, &ch, 1) > 0)
  617.     continue;
  618.     /* if there's an error closing at this point, not much we can do */
  619.     return(close(sock)); /* this is guarded */
  620. }
  621. #ifdef MAIN
  622. /*
  623.  * Use the chargen service to test input buffering directly.
  624.  * You may have to uncomment the `chargen' service description in your
  625.  * inetd.conf (and then SIGHUP inetd) for this to work.  */
  626. main()
  627. {
  628.     int   sock = SockOpen("localhost", 19, NULL);
  629.     char buf[80];
  630.     while (SockRead(sock, buf, sizeof(buf)-1))
  631. SockWrite(1, buf, strlen(buf));
  632.     SockClose(sock);
  633. }
  634. #endif /* MAIN */
  635. /* socket.c ends here */