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