A LOT updates. Cannot separate. :)
[silc.git] / lib / silcutil / silcnet.c
1 /*
2
3   silcnet.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2000 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /*
21  * $Id$
22  * $Log$
23  * Revision 1.2  2000/10/31 19:48:32  priikone
24  *      A LOT updates. Cannot separate. :)
25  *
26  * Revision 1.1  2000/09/13 17:45:16  priikone
27  *      Splitted SILC core library. Core library includes now only
28  *      SILC protocol specific stuff. New utility library includes the
29  *      old stuff from core library that is more generic purpose stuff.
30  *
31  * Revision 1.3  2000/07/05 06:06:35  priikone
32  *      Global cosmetic change.
33  *
34  * Revision 1.2  2000/06/30 10:49:48  priikone
35  *      Added SOCKS4 and SOCKS5 support for SILC client.
36  *
37  * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
38  *      Imported from internal CVS/Added Log headers.
39  *
40  *
41  */
42
43 #include "silcincludes.h"
44 #include "silcnet.h"
45
46 /* This function creates server or daemon or listener or what ever. This
47    does not fork a new process, it must be done by the caller if caller
48    wants to create a child process. This is used by the SILC server. 
49    If argument `ip_addr' is NULL `any' address will be used. Returns 
50    the created socket or -1 on error. */
51
52 int silc_net_create_server(int port, char *ip_addr)
53 {
54   int sock, rval;
55   struct sockaddr_in server;
56
57   SILC_LOG_DEBUG(("Creating a new server listener"));
58
59   /* Create the socket */
60   sock = socket(PF_INET, SOCK_STREAM, 0);
61   if (sock < 0) {
62     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
63     return -1;
64   }
65
66   /* Set the socket options */
67   rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
68   if (rval < 0) {
69     SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno)));
70     return -1;
71   }
72
73   /* Set the socket information for bind() */
74   memset(&server, 0, sizeof(server));
75   server.sin_family = PF_INET;
76   server.sin_port = htons(port);
77
78   /* Convert IP address to network byte order */
79   if (ip_addr)
80     inet_aton(ip_addr, &server.sin_addr);
81   else
82     server.sin_addr.s_addr = INADDR_ANY;
83
84   /* Bind the server socket */
85   rval = bind(sock, (struct sockaddr *)&server, sizeof(server));
86   if (rval < 0) {
87     SILC_LOG_ERROR(("Cannot bind socket: %s", strerror(errno)));
88     return -1;
89   }
90
91   /* Specify that we are listenning */
92   rval = listen(sock, 5);
93   if (rval < 0) {
94     SILC_LOG_ERROR(("Cannot set socket listenning: %s", strerror(errno)));
95     return -1;
96   }
97
98   /* Set the server socket to non-blocking mode */
99   silc_net_set_socket_nonblock(sock);
100
101   SILC_LOG_DEBUG(("Server listener created, fd=%d", sock));
102
103   return sock;
104 }
105
106 void silc_net_close_server(int sock)
107 {
108   shutdown(sock, 2);
109   close(sock);
110
111   SILC_LOG_DEBUG(("Server socket closed"));
112 }
113
114 /* Creates a connection (TCP/IP) to a remote host. Returns the connection
115    socket or -1 on error. This blocks the process while trying to create
116    the connection. */
117
118 int silc_net_create_connection(int port, char *host)
119 {
120   int sock, rval;
121   struct hostent *dest;
122   struct sockaddr_in desthost;
123
124   SILC_LOG_DEBUG(("Creating connection to host %s port %d", host, port));
125
126   /* Do host lookup */
127   dest = gethostbyname(host);
128   if (!dest) {
129     SILC_LOG_ERROR(("Network (%s) unreachable", host));
130     return -1;
131   }
132
133   /* Set socket information */
134   memset(&desthost, 0, sizeof(desthost));
135   desthost.sin_port = htons(port);
136   desthost.sin_family = PF_INET;
137   memcpy(&desthost.sin_addr, dest->h_addr_list[0], sizeof(desthost.sin_addr));
138
139   /* Create the connection socket */
140   sock = socket(PF_INET, SOCK_STREAM, 0);
141   if (sock < 0) {
142     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
143     return -1;
144   }
145
146   /* Connect to the host */
147   rval = connect(sock, (struct sockaddr *)&desthost, sizeof(desthost));
148   if (rval < 0) {
149     SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
150     shutdown(sock, 2);
151     close(sock);
152     return -1;
153   }
154
155   /* Set appropriate options */
156   silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
157   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
158
159   SILC_LOG_DEBUG(("Connection created"));
160
161   return sock;
162 }
163
164 /* Creates a connection (TCP/IP) to a remote host. Returns the connection
165    socket or -1 on error. This creates non-blocking socket hence the
166    connection returns directly. To get the result of the connect() one
167    must select() the socket and read the result after it's ready. */
168
169 int silc_net_create_connection_async(int port, char *host)
170 {
171   int sock, rval;
172   struct hostent *dest;
173   struct sockaddr_in desthost;
174
175   SILC_LOG_DEBUG(("Creating connection (async) to host %s port %d", 
176                   host, port));
177
178   /* Do host lookup */
179   dest = gethostbyname(host);
180   if (!dest) {
181     SILC_LOG_ERROR(("Network (%s) unreachable", host));
182     return -1;
183   }
184
185   /* Set socket information */
186   memset(&desthost, 0, sizeof(desthost));
187   desthost.sin_port = htons(port);
188   desthost.sin_family = PF_INET;
189   memcpy(&desthost.sin_addr, dest->h_addr_list[0], sizeof(desthost.sin_addr));
190
191   /* Create the connection socket */
192   sock = socket(PF_INET, SOCK_STREAM, 0);
193   if (sock < 0) {
194     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
195     return -1;
196   }
197
198   /* Set the socket to non-blocking mode */
199   silc_net_set_socket_nonblock(sock);
200
201   /* Connect to the host */
202   rval = connect(sock, (struct sockaddr *)&desthost, sizeof(desthost));
203   if (rval < 0) {
204     if (errno !=  EINPROGRESS) {
205       SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
206       shutdown(sock, 2);
207       close(sock);
208       return -1;
209     }
210   }
211
212   /* Set appropriate options */
213   silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
214   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
215
216   SILC_LOG_DEBUG(("Connection operation in progress"));
217
218   return sock;
219 }
220
221 /* Closes the connection */
222
223 void silc_net_close_connection(int sock)
224 {
225   close(sock);
226 }
227
228 /* Accepts a connection from a particular socket */
229
230 int silc_net_accept_connection(int sock)
231 {
232   return accept(sock, 0, 0);
233 }
234
235 /* Set's the socket to non-blocking mode. */
236
237 int silc_net_set_socket_nonblock(int sock)
238 {
239   return fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
240 }
241
242 /* Sets a option for a socket. */
243
244 int silc_net_set_socket_opt(int sock, int level, int option, int on)
245 {
246   return setsockopt(sock, level, option, (void *)&on, sizeof(on));
247 }
248
249 /* Checks whether IP address sent as argument is valid IP address. */
250
251 int silc_net_is_ip(const char *addr)
252 {
253   struct in_addr tmp;
254   return inet_aton(addr, &tmp);
255 }
256
257 /* Performs lookups for remote name and IP address. */
258
259 void silc_net_check_host_by_sock(int sock, char **hostname, char **ip)
260 {
261   struct sockaddr_in remote;
262   struct hostent *dest;
263   char *host_ip = NULL;
264   char host_name[1024];
265   int rval, len;
266   int i;
267
268   *hostname = NULL;
269   *ip = NULL;
270
271   SILC_LOG_DEBUG(("Resolving remote hostname and IP address"));
272
273   memset(&remote, 0, sizeof(remote));
274   len = sizeof(remote);
275   rval = getpeername(sock, (struct sockaddr *)&remote, &len);
276   if (rval < 0)
277     return;
278
279   /* Get host by address */
280   dest = gethostbyaddr((char *)&remote.sin_addr, 
281                        sizeof(struct in_addr), AF_INET);
282   if (!dest)
283     return;
284
285   /* Get same hsot by name to see that the remote host really is
286      the who it says it is */
287   memset(host_name, 0, sizeof(host_name));
288   memcpy(host_name, dest->h_name, strlen(dest->h_name));
289   dest = gethostbyname(host_name);
290   if (!dest)
291     return;
292
293   /* Find the address from list */
294   for (i = 0; dest->h_addr_list[i]; i++)
295     if (!memcmp(dest->h_addr_list[i], &remote.sin_addr, 
296                sizeof(struct in_addr)))
297       break;
298   if (!dest->h_addr_list[i])
299     return;
300
301   host_ip = inet_ntoa(remote.sin_addr);
302   if (!host_ip)
303     return;
304
305   *hostname = silc_calloc(strlen(host_name) + 1, sizeof(char));
306   memcpy(*hostname, host_name, strlen(host_name));
307   SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
308   *ip = silc_calloc(strlen(host_ip) + 1, sizeof(char));
309   memcpy(*ip, host_ip, strlen(host_ip));
310   SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
311 }
312
313 /* Return name of localhost. */
314
315 char *silc_net_localhost()
316 {
317   char hostname[256];
318   if (!gethostname(hostname, sizeof(hostname)))
319     return strdup(hostname);
320   return NULL;
321 }