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 *) ¬, 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