1 /*
2  *                               Alizarin Tetris
3  * Low level networking routines.
4  *
5  * Copyright 2000, Kiri Wagstaff & Westley Weimer
6  */
7 
8 #include <config.h>	/* go autoconf! */
9 #include "atris.h"
10 
11 #include <sys/types.h>
12 #include <unistd.h>
13 
14 #if HAVE_NETDB_H
15 #include <fcntl.h>
16 #endif
17 
18 #if HAVE_NETINET_IN_H
19 #include <netinet/in.h>
20 #endif
21 
22 #if HAVE_NETDB_H
23 #include <netdb.h>
24 #endif
25 
26 #include <time.h>
27 
28 #if HAVE_SYS_SOCKET_H
29 #include <sys/socket.h>
30 #else
31 #if HAVE_WINSOCK_H
32 #include <winsock.h>
33 #endif
34 #endif
35 
36 char *error_msg = NULL;
37 
38 /***************************************************************************
39  *	Server_AwaitConnection()
40  * Sets up the server side listening socket and awaits connections from the
41  * outside world. Returns a socket.
42  *********************************************************************PROTO*/
43 int
Server_AwaitConnection(int port)44 Server_AwaitConnection(int port)
45 {
46   static struct sockaddr_in addr;
47   static struct hostent *host;
48   static int sockListen=0;
49   static int addrLen;
50   int val1;
51   struct linger val2;
52   int sock;
53 
54   if (sockListen == 0) {
55     memset(&addr,0,sizeof(addr));
56     addr.sin_family=AF_INET;
57     addr.sin_addr.s_addr=htonl(INADDR_ANY);
58     addr.sin_port=htons(port);
59     sockListen=socket(AF_INET,SOCK_STREAM,0);
60 
61     error_msg = 0;
62 
63     if (sockListen < 0) {
64 	Debug("unable to create socket: %s\n",error_msg = strerror(errno));
65 	return -1;
66     }
67 
68     val1=1;
69 
70 #ifdef HAVE_SYS_SOCKET_H
71     if (fcntl(sockListen, F_SETFL, O_NONBLOCK)) {
72 	Debug("unable to make socket non-blocking: %s\n", error_msg = strerror(errno));
73 	return -1;
74     }
75 #endif
76 
77     if (setsockopt(sockListen,SOL_SOCKET,SO_REUSEADDR,
78 		   (void *)&val1, sizeof(val1))) {
79       Debug("WARNING: setsockopt(...,SO_REUSEADDR) failed. You may have to wait a\n"
80 	      "\tfew minutes before you can try to be the server again.\n");
81     }
82     if (bind(sockListen, (struct sockaddr *)&addr, sizeof(addr))) {
83 	Debug("unable to bind socket (for listening): %s\n", error_msg = strerror(errno));
84 	return -1;
85     }
86     if (listen(sockListen,16)) {
87 	Debug("unable to listen on socket: %s\n", error_msg = strerror(errno));
88 	return -1;
89     }
90 
91     addrLen=sizeof(addr);
92 
93     Debug("accepting connections on port %d, socketfd %d\n",port,sockListen);
94   }
95 #if HAVE_SELECT || HAVE_WINSOCK_H
96   /* it is possible to select() on a socket for the purpose of doing an
97    * accept by putting it in the read group */
98   {
99       fd_set read_fds;
100       struct timeval timeout = {0, 0};
101       int retval;
102 
103       FD_ZERO(&read_fds);
104       FD_SET(sockListen, &read_fds);
105 
106       retval = select(sockListen+1, &read_fds, NULL, NULL, &timeout);
107 
108       if (retval <= 0) {
109 	  /* no one is there */
110 	  return -1;
111       }
112   }
113 #endif
114   sock=accept(sockListen, (struct sockaddr *)&addr,&addrLen);
115   if (sock < 0) {
116       if (
117 #ifdef HAVE_SYS_SOCKET_H
118 	      errno == EWOULDBLOCK ||
119 #endif
120 	      errno == EAGAIN)
121 	  return -1;
122       else {
123 	  Debug("unable to accept on listening socket: %s\n", error_msg = strerror(errno));
124 	  return -1;
125       }
126   }
127 
128   val2.l_onoff=0; val2.l_linger=0;
129   if (setsockopt(sock,SOL_SOCKET,SO_LINGER,(void *)&val2,sizeof(val2)))
130       Debug("WARNING: setsockopt(...,SO_LINGER) failed. You may have to wait a few\n"
131 	      "\tminutes before you can try to be the server again.\n");
132 
133   host=gethostbyaddr((void *)&addr.sin_addr,sizeof(struct in_addr),AF_INET);
134   if (!host)
135       Debug("connection from some unresolvable IP address, socketfd %d.\n", sock);
136   else
137       Debug("connection from %s, socketfd %d.\n",host->h_name,sock);
138   return sock;
139 }
140 
141 /***************************************************************************
142  *	Client_Connect()
143  * Set up client side networking and connect to the server. Returns a socket
144  * pointing to the server.
145  *
146  * On error, -1 is returned.
147  *********************************************************************PROTO*/
148 int
Client_Connect(char * hoststr,int lport)149 Client_Connect(char *hoststr, int lport)
150 {
151     struct sockaddr_in addr;
152     struct hostent *host;
153     int mySock;
154     short port = lport;
155     int retval;
156 
157     error_msg = NULL;
158 
159     host=gethostbyname(hoststr);
160     if (!host) {
161 	Debug("unable to resolve [%s]: unknown hostname\n",hoststr);
162 	error_msg = "unknown hostname";
163 	return -1;
164     }
165 
166     Debug("connecting to %s:%d ...\n",host->h_name,port);
167 
168     memset(&addr, 0, sizeof(addr));
169     addr.sin_family = AF_INET; /*host->h_addrtype;*/
170     memcpy(&addr.sin_addr, host->h_addr, host->h_length);
171     addr.sin_port=htons(port);
172     mySock = socket(AF_INET, SOCK_STREAM, 0);
173     if (mySock < 0) {
174 	Debug("unable to create socket: %s\n",error_msg = strerror(errno));
175 	return -1;
176     }
177 
178     retval = connect(mySock, (struct sockaddr *) &addr, sizeof(addr));
179 
180     if (retval < 0) {
181 	Debug("unable to connect: %s\n",error_msg = strerror(errno));
182 	close(mySock);
183 	return -1;
184     }
185 
186     Debug("connected to %s:%d, socketfd %d.\n",host->h_name,port,mySock);
187     return mySock;
188 }
189 
190 /***************************************************************************
191  *	Network_Init()
192  * Call before you try to use any networking functions. Returns 0 on
193  * success.
194  *********************************************************************PROTO*/
195 int
Network_Init(void)196 Network_Init(void)
197 {
198 #if HAVE_WINSOCK_H
199     WORD version_wanted = MAKEWORD(1,1);
200     WSADATA wsaData;
201 
202     if ( WSAStartup(version_wanted, &wsaData) != 0 ) {
203 	Debug("WARNING: Couldn't initialize Winsock 1.1\n");
204 	return 1;
205     }
206     Debug("Winsock 1.1 networking available.\n");
207     return 0;
208 #endif
209     /* always works on Unix ... */
210     return 0;
211 }
212 
213 /***************************************************************************
214  *	Network_Quit()
215  * Call when you are done networking.
216  *********************************************************************PROTO*/
217 void
Network_Quit(void)218 Network_Quit(void)
219 {
220 #if HAVE_WINSOCK_H
221     /* Clean up windows networking */
222     if ( WSACleanup() == SOCKET_ERROR ) {
223 	if ( WSAGetLastError() == WSAEINPROGRESS ) {
224 	    WSACancelBlockingCall();
225 	    WSACleanup();
226 	}
227     }
228 #endif
229     return;
230 }
231