home *** CD-ROM | disk | FTP | other *** search
/ Borland Programmer's Resource / Borland_Programmers_Resource_CD_1995.iso / ntcode / blat01 / gensock / gensock.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-19  |  17.4 KB  |  856 lines

  1. // -*- C++ -*-
  2. // generic socket DLL, winsock version
  3. // disclaimer:  a C programmer wrote this.
  4.  
  5. // $Id: gensock.cpp 1.13 1994/08/25 22:44:23 rushing Exp rushing $
  6.  
  7. #include <windows.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <ctype.h>
  11.  
  12. extern "C" {
  13. #include <winsock.h>
  14. #include "gensock.h"
  15. }
  16.  
  17. #define SOCKET_BUFFER_SIZE    512
  18.  
  19. /* This is for NT */
  20. #ifdef WIN32
  21. HANDLE    dll_module_handle;
  22. #define    GET_CURRENT_TASK    dll_module_handle
  23. #define    TASK_HANDLE_TYPE    HANDLE
  24. #define GENSOCK_EXPORT
  25.  
  26. /* This is for WIN16 */
  27. #else
  28. HINSTANCE dll_module_handle;
  29. #define    GET_CURRENT_TASK    GetCurrentTask()
  30. #define    TASK_HANDLE_TYPE    HTASK
  31. #define GENSOCK_EXPORT        _export
  32. #endif
  33.  
  34. int  init_winsock (void);
  35. void deinit_winsock (void);
  36.  
  37. //
  38. //
  39. //
  40.  
  41. #ifdef _DEBUG
  42. void complain (char * message)
  43. {
  44.   OutputDebugString (message);
  45. }
  46. #else
  47. void complain (char * message)
  48. {
  49.   MessageBox (NULL, message, "GENSOCK.DLL Error", MB_OK|MB_ICONHAND);
  50. }
  51. #endif
  52.  
  53. //
  54. // ---------------------------------------------------------------------------
  55. // container for a buffered SOCK_STREAM.
  56.  
  57. class connection
  58. {
  59.  private:
  60.   SOCKET    the_socket;
  61.   char *    in_buffer;
  62.   char *    out_buffer;
  63.   unsigned int    in_index;
  64.   unsigned int    out_index;
  65.   unsigned int    in_buffer_total;
  66.   unsigned int    out_buffer_total;
  67.   unsigned int    last_winsock_error;
  68.   TASK_HANDLE_TYPE        owner_task;
  69.   fd_set    fds;
  70.   struct timeval    timeout;
  71.  
  72.  public:
  73.  
  74.   connection (void);
  75.   ~connection (void);
  76.  
  77.   int         get_connected (char * hostname, char * service);
  78.   SOCKET     get_socket(void) { return (the_socket); }
  79.   TASK_HANDLE_TYPE        get_owner_task(void) { return (owner_task); }
  80.   int        get_buffer(int wait);
  81.   int        close (void);
  82.   int        getchar (int wait, char * ch);
  83.   int        put_data (char * data, unsigned long length);
  84.   int        put_data_buffered (char * data, unsigned long length);
  85.   int        put_data_flush (void);
  86. };
  87.  
  88. connection::connection (void)
  89. {
  90.   the_socket = 0;
  91.   in_index = 0;
  92.   out_index = 0;
  93.   in_buffer_total = 0;
  94.   out_buffer_total = 0;
  95.   in_buffer = 0;
  96.  
  97.   in_buffer = new char[SOCKET_BUFFER_SIZE];
  98.   out_buffer = new char[SOCKET_BUFFER_SIZE];
  99.  
  100.   last_winsock_error = 0;
  101. }
  102.  
  103. connection::~connection (void)
  104. {
  105.   delete [] in_buffer;
  106. }
  107.  
  108. int
  109. gensock_is_a_number (char * string)
  110. {
  111.   while (*string) {
  112.     if (!isdigit (*string)) {
  113.       return (0);
  114.     }
  115.     string++;
  116.   }
  117.   return (1);
  118. }
  119.     
  120. //
  121. // ---------------------------------------------------------------------------
  122. //
  123.  
  124. int
  125. connection::get_connected (char FAR * hostname, char FAR * service)
  126. {
  127.   struct hostent FAR *    hostentry;
  128.   struct servent FAR *    serventry;
  129.   unsigned long     ip_address;
  130.   struct sockaddr_in    sa_in;
  131.   int            our_port;
  132.   int            not = 0;
  133.   int            retval, err_code;
  134.   unsigned long        ioctl_blocking = 1;
  135.   char            message[512];
  136.  
  137.   // if the ctor couldn't get a buffer
  138.   if (!in_buffer || !out_buffer)
  139.     return (ERR_CANT_MALLOC);
  140.  
  141.   // --------------------------------------------------
  142.   // resolve the service name
  143.   //
  144.  
  145.   // If they've specified a number, just use it.
  146.   if (gensock_is_a_number (service)) {
  147.     char * tail;
  148.     our_port = (int) strtol (service, &tail, 10);
  149.     if (tail == service) {
  150.       return (ERR_CANT_RESOLVE_SERVICE);
  151.     } else {
  152.       our_port = htons (our_port);
  153.     }
  154.   } else {
  155.     // we have a name, we must resolve it.
  156.     serventry = getservbyname (service, (LPSTR)"tcp");
  157.     
  158.     if (serventry)
  159.       our_port = serventry->s_port;
  160.     else {
  161.       retval = WSAGetLastError();
  162.       // Chicago beta is throwing a WSANO_RECOVERY here...
  163.       if ((retval == WSANO_DATA) || (retval == WSANO_RECOVERY)) {
  164.     return (ERR_CANT_RESOLVE_SERVICE);
  165.       } else {
  166.     return (retval - 5000);
  167.       }
  168.     }
  169.   }
  170.  
  171.   // --------------------------------------------------
  172.   // resolve the hostname/ipaddress
  173.   //
  174.  
  175.   if ((ip_address = inet_addr (hostname)) != INADDR_NONE) {
  176.     sa_in.sin_addr.s_addr = ip_address;
  177.   }
  178.   else {
  179.     if ((hostentry = gethostbyname(hostname)) == NULL) {
  180.       return (ERR_CANT_RESOLVE_HOSTNAME);
  181.     }
  182.     sa_in.sin_addr.s_addr = *(long far *)hostentry->h_addr;
  183.   }
  184.  
  185.  
  186.   // --------------------------------------------------
  187.   // get a socket
  188.   //
  189.  
  190.   if ((the_socket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
  191.     return (ERR_CANT_GET_SOCKET);
  192.   }
  193.  
  194.   sa_in.sin_family = AF_INET;
  195.   sa_in.sin_port = our_port;
  196.  
  197.   // set socket options.  DONTLINGER will give us a more graceful disconnect
  198.  
  199.   setsockopt(the_socket,
  200.          SOL_SOCKET,
  201.          SO_DONTLINGER,
  202.          (char *) ¬, sizeof(not));
  203.  
  204.   // get a connection
  205.  
  206.   if ((retval = connect (the_socket,
  207.              (struct sockaddr *)&sa_in,
  208.              sizeof(struct sockaddr_in))==SOCKET_ERROR)) {
  209.     switch ((err_code = WSAGetLastError())) {
  210.       /* twiddle our thumbs until the connect succeeds */
  211.     case WSAEWOULDBLOCK:
  212.       break;
  213.     case WSAECONNREFUSED:
  214.       return (ERR_CONNECTION_REFUSED);
  215.       break;
  216.     default:
  217.       wsprintf(message, "unexpected error %d from winsock\n", err_code);
  218.       complain(message);
  219.       return (ERR_CANT_CONNECT);
  220.       break;
  221.     }
  222.   }
  223.  
  224.   owner_task = GET_CURRENT_TASK;
  225.  
  226.   // Make this a non-blocking socket
  227.   ioctlsocket (the_socket, FIONBIO, &ioctl_blocking);
  228.  
  229.   // make the FD_SET and timeout structures for later operations...
  230.  
  231.   FD_ZERO (&fds);
  232.   FD_SET  (the_socket, &fds);
  233.  
  234.   // normal timeout, can be changed by the wait option.
  235.   timeout.tv_sec = 30;
  236.   timeout.tv_usec = 0;
  237.  
  238.   return (0);
  239. }
  240.  
  241.  
  242. //
  243. //---------------------------------------------------------------------------
  244. //
  245. // The 'wait' parameter, if set, says to return WAIT_A_BIT
  246. // if there's no data waiting to be read.
  247.  
  248. int
  249. connection::get_buffer(int wait)
  250. {
  251.   int retval;
  252.   int bytes_read = 0;
  253.   unsigned long ready_to_read = 0;
  254.  
  255.   // Use select to see if data is waiting...
  256.  
  257.   FD_ZERO (&fds);
  258.   FD_SET  (the_socket, &fds);
  259.  
  260.   // if wait is set, we are polling, return immediately
  261.   if (wait) {
  262.     timeout.tv_sec = 0;
  263.   }
  264.   else {
  265.     timeout.tv_sec = 30;
  266.   }
  267.  
  268.   if ((retval = select (0, &fds, NULL, NULL, &timeout))
  269.       == SOCKET_ERROR) {
  270.     char what_error[256];
  271.     int error_code = WSAGetLastError();
  272.  
  273.     if (error_code == WSAEINPROGRESS && wait) {
  274.       return (WAIT_A_BIT);
  275.     }
  276.  
  277.     wsprintf (what_error,
  278.           "connection::get_buffer() unexpected error from select: %d",
  279.           error_code);
  280.     complain (what_error);
  281.   }
  282.  
  283.   // if we don't want to wait
  284.   if (!retval && wait) {
  285.     return (WAIT_A_BIT);
  286.   }
  287.  
  288.   // we have data waiting...
  289.   bytes_read = recv (the_socket,
  290.              in_buffer,
  291.              SOCKET_BUFFER_SIZE,
  292.              0);
  293.  
  294.   // just in case.
  295.  
  296.   if (bytes_read == 0) {
  297.     // connection terminated (semi-) gracefully by the other side
  298.     return (ERR_NOT_CONNECTED);
  299.   }
  300.  
  301.   if (bytes_read == SOCKET_ERROR) {
  302.     char what_error[256];
  303.     int ws_error = WSAGetLastError();
  304.     switch (ws_error) {
  305.       // all these indicate loss of connection (are there more?)
  306.     case WSAENOTCONN:
  307.     case WSAENETDOWN:
  308.     case WSAENETUNREACH:
  309.     case WSAENETRESET:
  310.     case WSAECONNABORTED:
  311.     case WSAECONNRESET:
  312.       return (ERR_NOT_CONNECTED);
  313.       break;
  314.  
  315.     case WSAEWOULDBLOCK:
  316.       return (WAIT_A_BIT);
  317.       break;
  318.  
  319.     default:
  320.       wsprintf (what_error,
  321.         "connection::get_buffer() unexpected error: %d",
  322.         ws_error);
  323.       complain (what_error);
  324.     }
  325.   }
  326.  
  327.   // reset buffer indices.
  328.   in_buffer_total = bytes_read;
  329.   in_index = 0;
  330.   return (0);
  331.  
  332. }
  333.  
  334. //
  335. //---------------------------------------------------------------------------
  336. // get a character from this connection.
  337. //
  338.  
  339. int
  340. connection::getchar(int wait, char FAR * ch)
  341. {
  342.   int retval;
  343.  
  344.   if (in_index >= in_buffer_total) {
  345.     if ((retval = get_buffer(wait)))
  346.       return (retval);
  347.   }
  348.   *ch = in_buffer[in_index++];
  349.   return (0);
  350. }
  351.  
  352.  
  353. //
  354. //---------------------------------------------------------------------------
  355. // FIXME: should try to handle the fact that send can only take
  356. // an int, not an unsigned long.
  357.  
  358. int
  359. connection::put_data (char * data, unsigned long length)
  360. {
  361.   int num_sent;
  362.   int retval;
  363.  
  364.   FD_ZERO (&fds);
  365.   FD_SET  (the_socket, &fds);
  366.  
  367.   timeout.tv_sec = 30;
  368.  
  369.   while (length > 0) {
  370.     if ((retval = select (0, NULL, &fds, NULL, &timeout)) == SOCKET_ERROR) {
  371.       char what_error[256];
  372.       int error_code = WSAGetLastError();
  373.       
  374.       wsprintf (what_error,
  375.         "connection::put_data() unexpected error from select: %d",
  376.         error_code);
  377.       complain (what_error);
  378.     }
  379.  
  380.     num_sent = send (the_socket,
  381.              data,
  382.              length > 1024 ? 1024 : (int)length,
  383.              0);
  384.  
  385.     if (num_sent == SOCKET_ERROR) {
  386.       char what_error[256];
  387.       int ws_error = WSAGetLastError();
  388.       switch (ws_error) {
  389.     // this is the only error we really expect to see.
  390.       case WSAENOTCONN:
  391.     return (ERR_NOT_CONNECTED);
  392.     break;
  393.  
  394.     // seems that we can still get a block
  395.       case WSAEWOULDBLOCK:
  396.     break;
  397.  
  398.       default:
  399.     wsprintf (what_error,
  400.           "connection::put_data() unexpected error from send(): %d",
  401.           ws_error);
  402.     complain (what_error);
  403.     return (ERR_SENDING_DATA);
  404.       }
  405.     }
  406.     else {
  407.       length -= num_sent;
  408.       data += num_sent;
  409.     }
  410.   }
  411.  
  412.   return (0);
  413. }
  414.  
  415. //
  416. //
  417. // buffered output
  418. //
  419.  
  420. int
  421. connection::put_data_buffered (char * data, unsigned long length)
  422. {
  423.   unsigned int sorta_sent = 0;
  424.   int retval;
  425.  
  426.   while (length) {
  427.     if ((out_index + length) < SOCKET_BUFFER_SIZE) {
  428.       // we won't overflow, simply copy into the buffer
  429.       memcpy (out_buffer + out_index, data, length);
  430.       out_index += length;
  431.       length = 0;
  432.     }
  433.     else {
  434.       unsigned int orphaned_chunk = SOCKET_BUFFER_SIZE - out_index;
  435.       // we will overflow, handle it
  436.       memcpy (out_buffer + out_index, data, orphaned_chunk);
  437.       // send this buffer...
  438.       if ((retval = put_data (out_buffer, SOCKET_BUFFER_SIZE))) {
  439.     return (retval);
  440.       }
  441.       length -= orphaned_chunk;
  442.       out_index = 0;
  443.       data += orphaned_chunk;
  444.     }
  445.   }
  446.  
  447.   return (0);
  448. }
  449.  
  450. int
  451. connection::put_data_flush (void)
  452. {
  453.   int retval;
  454.  
  455.   if ((retval = put_data (out_buffer, out_index)))
  456.     return (retval);
  457.   else
  458.     out_index = 0;
  459.  
  460.   return(0);
  461. }
  462.  
  463. //
  464. //---------------------------------------------------------------------------
  465. //
  466.  
  467. int
  468. connection::close (void)
  469. {
  470.   if (closesocket(the_socket) == SOCKET_ERROR)
  471.     return (ERR_CLOSING);
  472.   else
  473.     return (0);
  474. }
  475.  
  476.  
  477. //
  478. //---------------------------------------------------------------------------
  479. // we keep lists of connections in this class
  480.  
  481. class connection_list
  482. {
  483. private:
  484.   connection *         data;
  485.   connection_list *     next;
  486.  
  487. public:
  488.   connection_list     (void);
  489.   ~connection_list    (void);
  490.   void push         (connection & conn);
  491.  
  492.   // should really use pointer-to-memberfun for these
  493.   connection * find    (SOCKET sock);
  494.   int how_many_are_mine    (void);
  495.  
  496.   void remove        (socktag sock);
  497. };
  498.  
  499. connection_list::connection_list (void)
  500. {
  501.   next = 0;
  502. }
  503.  
  504. connection_list::~connection_list(void)
  505. {
  506.   delete data;
  507. }
  508.  
  509. // add a new connection to the list
  510.  
  511. void
  512. connection_list::push (connection & conn)
  513. {
  514.   connection_list * new_conn;
  515.  
  516.   new_conn = new connection_list();
  517.  
  518.   new_conn->data = data;
  519.   new_conn->next = next;
  520.  
  521.   data = &conn;
  522.   next = new_conn;
  523.  
  524. }
  525.  
  526. int
  527. connection_list::how_many_are_mine(void)
  528. {
  529.   TASK_HANDLE_TYPE    current_task = GET_CURRENT_TASK;
  530.   connection_list * iter = this;
  531.   int num = 0;
  532.  
  533.   while (iter->data) {
  534.     if (iter->data->get_owner_task() == current_task)
  535.       num++;
  536.     iter = iter->next;
  537.   }
  538.   return (num);
  539. }
  540.  
  541. // find a particular socket's connection object.
  542.  
  543. connection *
  544. connection_list::find (SOCKET sock)
  545. {
  546.   connection_list * iter = this;
  547.  
  548.   while (iter->data) {
  549.     if (iter->data->get_socket() == sock)
  550.       return (iter->data);
  551.     iter = iter->next;
  552.   }
  553.   return (0);
  554. }
  555.  
  556. void
  557. connection_list::remove (socktag sock)
  558. {
  559.   // at the end
  560.   if (!data)
  561.     return;
  562.  
  563.   // we can assume next is valid because
  564.   // the last node is always {0,0}
  565.   if (data == sock) {
  566.     delete data;
  567.     data = next->data;
  568.     next = next->next;    // 8^)
  569.     return;
  570.   }
  571.  
  572.   // recurse
  573.   next->remove(sock);
  574. }
  575.  
  576. //
  577. // ---------------------------------------------------------------------------
  578. // global variables (shared by all DLL users)
  579.  
  580. connection_list global_socket_list;
  581. int    network_initialized;
  582.  
  583. //
  584. //---------------------------------------------------------------------------
  585. //
  586.  
  587. #ifndef WIN32
  588.  
  589. // the DLL entry routine
  590. int FAR PASCAL LibMain (HINSTANCE hinstance,
  591.             WPARAM data_seg,
  592.             LPARAM heap_size,
  593.             LPSTR command_line)
  594. {
  595.   network_initialized = 0;
  596.   dll_module_handle = hinstance;
  597.   return (1);
  598. }
  599.  
  600. #else
  601.  
  602. extern "C" {
  603.   INT APIENTRY
  604.     LibMain (HANDLE     hInst,
  605.          ULONG        reason_called,
  606.          LPVOID        reserved)
  607.       {
  608.  
  609.     switch (reason_called) {
  610.     case DLL_PROCESS_ATTACH:
  611.       /* init */
  612.       dll_module_handle = hInst;
  613.       break;
  614.     case DLL_THREAD_ATTACH:
  615.       break;
  616.     case DLL_THREAD_DETACH:
  617.       break;
  618.     case DLL_PROCESS_DETACH:
  619.       break;
  620.  
  621.     default:
  622.       break;
  623.     }
  624.     return (1);
  625.       }
  626.  
  627.   /*
  628.    * This wrapper is the actual entry point for the DLL.  It ensures
  629.    * that the C RTL is correctly [de]initialized.
  630.    */
  631.  
  632. BOOL WINAPI _CRT_INIT (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved);
  633.  
  634. BOOL WINAPI
  635. dll_entry_point (HINSTANCE hinstDLL,
  636.          DWORD fdwReason,
  637.          LPVOID lpReserved)
  638. {
  639.   /* Init the C run-time before calling any of your code */
  640.  
  641.   switch (fdwReason) {
  642.   case DLL_PROCESS_ATTACH:
  643.   case DLL_THREAD_ATTACH:
  644.     if (!_CRT_INIT (hinstDLL, fdwReason, lpReserved))
  645.       return (FALSE);
  646.     else
  647.       LibMain (hinstDLL, fdwReason, lpReserved);
  648.     break;
  649.  
  650.   case DLL_PROCESS_DETACH:
  651.   case DLL_THREAD_DETACH:
  652.     if (!_CRT_INIT(hinstDLL, fdwReason, lpReserved))
  653.       return(FALSE);
  654.     break;
  655.   }
  656.   return (TRUE);
  657. }
  658.  
  659. }
  660. #endif
  661.  
  662. // ---------------------------------------------------------------------------
  663. // C/DLL interface
  664. //
  665.  
  666. int FAR PASCAL GENSOCK_EXPORT
  667. gensock_connect (char FAR * hostname,
  668.          char FAR * service,
  669.          socktag FAR * pst)
  670. {
  671.   int retval;
  672.   connection * conn = new connection;
  673.  
  674.   if (!conn)
  675.     return (ERR_INITIALIZING);
  676.  
  677.   // if this task hasn't opened any sockets yet, then
  678.   // call WSAStartup()
  679.  
  680.   if (global_socket_list.how_many_are_mine() < 1)
  681.     init_winsock();
  682.  
  683.   global_socket_list.push(*conn);
  684.  
  685.   if ((retval = conn->get_connected (hostname, service))) {
  686.     gensock_close(conn);
  687.     *pst = 0;
  688.     return (retval);
  689.   }
  690.   *pst = (void FAR *) conn;
  691.  
  692.   return (0);
  693. }
  694.  
  695. //
  696. //
  697. //
  698.  
  699. int FAR PASCAL GENSOCK_EXPORT
  700. gensock_getchar (socktag st, int wait, char FAR * ch)
  701. {
  702.   connection * conn;
  703.   int retval = 0;
  704.  
  705.   conn = (connection *) st;
  706.   if (!conn)
  707.     return (ERR_NOT_A_SOCKET);
  708.  
  709.   if ((retval = conn->getchar(wait, ch)))
  710.     return (retval);
  711.   else
  712.     return (0);
  713. }
  714.  
  715.  
  716. //---------------------------------------------------------------------------
  717. //
  718. //
  719.  
  720. int FAR PASCAL GENSOCK_EXPORT
  721. gensock_put_data (socktag st, char FAR * data, unsigned long length)
  722. {
  723.   connection * conn;
  724.   int retval = 0;
  725.  
  726.   conn = (connection *) st;
  727.  
  728.   if (!conn)
  729.     return (ERR_NOT_A_SOCKET);
  730.  
  731.   if ((retval = conn->put_data(data, length)))
  732.     return (retval);
  733.  
  734.   return (0);
  735. }
  736.  
  737. //---------------------------------------------------------------------------
  738. //
  739. //
  740.  
  741. int FAR PASCAL GENSOCK_EXPORT
  742. gensock_put_data_buffered (socktag st, char FAR * data, unsigned long length)
  743. {
  744.   connection * conn;
  745.   int retval = 0;
  746.  
  747.   conn = (connection *) st;
  748.  
  749.   if (!conn)
  750.     return (ERR_NOT_A_SOCKET);
  751.  
  752.   if ((retval = conn->put_data_buffered (data, length)))
  753.     return (retval);
  754.  
  755.   return (0);
  756. }
  757.  
  758. //---------------------------------------------------------------------------
  759. //
  760. //
  761.  
  762. int FAR PASCAL GENSOCK_EXPORT
  763. gensock_put_data_flush (socktag st)
  764. {
  765.   connection * conn;
  766.   int retval = 0;
  767.  
  768.   conn = (connection *) st;
  769.  
  770.   if (!conn)
  771.     return (ERR_NOT_A_SOCKET);
  772.  
  773.   if ((retval = conn->put_data_flush() ))
  774.     return (retval);
  775.  
  776.   return (0);
  777. }
  778.  
  779. //---------------------------------------------------------------------------
  780. //
  781. //
  782. int FAR PASCAL GENSOCK_EXPORT
  783. gensock_gethostname (char FAR * name, int namelen)
  784. {
  785.   int retval;
  786.   if ((retval = gethostname(name, namelen))) {
  787.     return (retval - 5000);
  788.   }
  789.   else return (0);
  790. }
  791.  
  792. //---------------------------------------------------------------------------
  793. //
  794. //
  795.  
  796. int FAR PASCAL GENSOCK_EXPORT
  797. gensock_close (socktag st)
  798. {
  799.   connection * conn;
  800.   int retval;
  801.  
  802.   conn = (connection *) st;
  803.  
  804.   if (!conn)
  805.     return (ERR_NOT_A_SOCKET);
  806.  
  807.   if ((retval = conn->close()))
  808.     return (retval);
  809.  
  810.   global_socket_list.remove((connection *)st);
  811.  
  812.   if (global_socket_list.how_many_are_mine() < 1) {
  813.     deinit_winsock();
  814.   }
  815.  
  816.   return (0);
  817. }
  818.  
  819. //---------------------------------------------------------------------------
  820. //
  821. //
  822.  
  823. int
  824. init_winsock(void)
  825. {
  826.   int retval;
  827.   WSADATA winsock_data;
  828.   WORD version_required = 0x0101; /* Version 1.1 */
  829.  
  830.   retval = WSAStartup (version_required, &winsock_data);
  831.  
  832.   switch (retval) {
  833.   case 0:
  834.     /* successful */
  835.     break;
  836.   case WSASYSNOTREADY:
  837.     return (ERR_SYS_NOT_READY);
  838.     break;
  839.   case WSAEINVAL:
  840.     return (ERR_EINVAL);
  841.     break;
  842.   case WSAVERNOTSUPPORTED:
  843.     return (ERR_VER_NOT_SUPPORTED);
  844.     break;
  845.   }
  846.   network_initialized = 1;
  847.   return (0);
  848. }
  849.  
  850. void
  851. deinit_winsock(void)
  852. {
  853.   network_initialized = 0;
  854.   WSACleanup();
  855. }
  856.