Code auditing weekend results and fixes committing.
[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 /* $Id$ */
21
22 #include "silcincludes.h"
23 #include "silcnet.h"
24
25 /* This function creates server or daemon or listener or what ever. This
26    does not fork a new process, it must be done by the caller if caller
27    wants to create a child process. This is used by the SILC server. 
28    If argument `ip_addr' is NULL `any' address will be used. Returns 
29    the created socket or -1 on error. */
30
31 int silc_net_create_server(int port, char *ip_addr)
32 {
33   int sock, rval;
34   struct sockaddr_in server;
35
36   SILC_LOG_DEBUG(("Creating a new server listener"));
37
38   /* Create the socket */
39   sock = socket(PF_INET, SOCK_STREAM, 0);
40   if (sock < 0) {
41     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
42     return -1;
43   }
44
45   /* Set the socket options */
46   rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
47   if (rval < 0) {
48     SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno)));
49     return -1;
50   }
51
52   /* Set the socket information for bind() */
53   memset(&server, 0, sizeof(server));
54   server.sin_family = PF_INET;
55   server.sin_port = htons(port);
56
57   /* Convert IP address to network byte order */
58   if (ip_addr)
59     inet_aton(ip_addr, &server.sin_addr);
60   else
61     server.sin_addr.s_addr = INADDR_ANY;
62
63   /* Bind the server socket */
64   rval = bind(sock, (struct sockaddr *)&server, sizeof(server));
65   if (rval < 0) {
66     SILC_LOG_ERROR(("Cannot bind socket: %s", strerror(errno)));
67     return -1;
68   }
69
70   /* Specify that we are listenning */
71   rval = listen(sock, 5);
72   if (rval < 0) {
73     SILC_LOG_ERROR(("Cannot set socket listenning: %s", strerror(errno)));
74     return -1;
75   }
76
77   /* Set the server socket to non-blocking mode */
78   silc_net_set_socket_nonblock(sock);
79
80   SILC_LOG_DEBUG(("Server listener created, fd=%d", sock));
81
82   return sock;
83 }
84
85 void silc_net_close_server(int sock)
86 {
87   shutdown(sock, 2);
88   close(sock);
89
90   SILC_LOG_DEBUG(("Server socket closed"));
91 }
92
93 /* Creates a connection (TCP/IP) to a remote host. Returns the connection
94    socket or -1 on error. This blocks the process while trying to create
95    the connection. */
96
97 int silc_net_create_connection(int port, char *host)
98 {
99   int sock, rval;
100   struct hostent *dest;
101   struct sockaddr_in desthost;
102
103   SILC_LOG_DEBUG(("Creating connection to host %s port %d", host, port));
104
105   /* Do host lookup */
106   dest = gethostbyname(host);
107   if (!dest) {
108     SILC_LOG_ERROR(("Network (%s) unreachable", host));
109     return -1;
110   }
111
112   /* Set socket information */
113   memset(&desthost, 0, sizeof(desthost));
114   desthost.sin_port = htons(port);
115   desthost.sin_family = PF_INET;
116   memcpy(&desthost.sin_addr, dest->h_addr_list[0], sizeof(desthost.sin_addr));
117
118   /* Create the connection socket */
119   sock = socket(PF_INET, SOCK_STREAM, 0);
120   if (sock < 0) {
121     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
122     return -1;
123   }
124
125   /* Connect to the host */
126   rval = connect(sock, (struct sockaddr *)&desthost, sizeof(desthost));
127   if (rval < 0) {
128     SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
129     shutdown(sock, 2);
130     close(sock);
131     return -1;
132   }
133
134   /* Set appropriate options */
135   silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
136   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
137
138   SILC_LOG_DEBUG(("Connection created"));
139
140   return sock;
141 }
142
143 /* Creates a connection (TCP/IP) to a remote host. Returns the connection
144    socket or -1 on error. This creates non-blocking socket hence the
145    connection returns directly. To get the result of the connect() one
146    must select() the socket and read the result after it's ready. */
147
148 int silc_net_create_connection_async(int port, char *host)
149 {
150   int sock, rval;
151   struct hostent *dest;
152   struct sockaddr_in desthost;
153
154   SILC_LOG_DEBUG(("Creating connection (async) to host %s port %d", 
155                   host, port));
156
157   /* Do host lookup */
158   dest = gethostbyname(host);
159   if (!dest) {
160     SILC_LOG_ERROR(("Network (%s) unreachable", host));
161     return -1;
162   }
163
164   /* Set socket information */
165   memset(&desthost, 0, sizeof(desthost));
166   desthost.sin_port = htons(port);
167   desthost.sin_family = PF_INET;
168   memcpy(&desthost.sin_addr, dest->h_addr_list[0], sizeof(desthost.sin_addr));
169
170   /* Create the connection socket */
171   sock = socket(PF_INET, SOCK_STREAM, 0);
172   if (sock < 0) {
173     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
174     return -1;
175   }
176
177   /* Set the socket to non-blocking mode */
178   silc_net_set_socket_nonblock(sock);
179
180   /* Connect to the host */
181   rval = connect(sock, (struct sockaddr *)&desthost, sizeof(desthost));
182   if (rval < 0) {
183     if (errno !=  EINPROGRESS) {
184       SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
185       shutdown(sock, 2);
186       close(sock);
187       return -1;
188     }
189   }
190
191   /* Set appropriate options */
192   silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
193   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
194
195   SILC_LOG_DEBUG(("Connection operation in progress"));
196
197   return sock;
198 }
199
200 /* Closes the connection */
201
202 void silc_net_close_connection(int sock)
203 {
204   close(sock);
205 }
206
207 /* Accepts a connection from a particular socket */
208
209 int silc_net_accept_connection(int sock)
210 {
211   return accept(sock, 0, 0);
212 }
213
214 /* Set's the socket to non-blocking mode. */
215
216 int silc_net_set_socket_nonblock(int sock)
217 {
218   return fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
219 }
220
221 /* Sets a option for a socket. */
222
223 int silc_net_set_socket_opt(int sock, int level, int option, int on)
224 {
225   return setsockopt(sock, level, option, (void *)&on, sizeof(on));
226 }
227
228 /* Checks whether IP address sent as argument is valid IP address. */
229
230 int silc_net_is_ip(const char *addr)
231 {
232   struct in_addr tmp;
233   return inet_aton(addr, &tmp);
234 }
235
236 /* Performs lookups for remote name and IP address. This peforms reverse
237    lookup as well to verify that the IP has FQDN. */
238
239 void silc_net_check_host_by_sock(int sock, char **hostname, char **ip)
240 {
241   struct sockaddr_in remote;
242   struct hostent *dest;
243   char *host_ip = NULL;
244   char host_name[1024];
245   int rval, len;
246   int i;
247
248   *hostname = NULL;
249   *ip = NULL;
250
251   SILC_LOG_DEBUG(("Resolving remote hostname and IP address"));
252
253   memset(&remote, 0, sizeof(remote));
254   len = sizeof(remote);
255   rval = getpeername(sock, (struct sockaddr *)&remote, &len);
256   if (rval < 0)
257     return;
258
259   /* Get host by address */
260   dest = gethostbyaddr((char *)&remote.sin_addr, 
261                        sizeof(struct in_addr), AF_INET);
262   if (!dest)
263     return;
264
265   /* Get same host by name to see that the remote host really is
266      the who it says it is */
267   memset(host_name, 0, sizeof(host_name));
268   memcpy(host_name, dest->h_name, strlen(dest->h_name));
269   dest = gethostbyname(host_name);
270   if (!dest)
271     return;
272
273   /* Find the address from list */
274   for (i = 0; dest->h_addr_list[i]; i++)
275     if (!memcmp(dest->h_addr_list[i], &remote.sin_addr, 
276                sizeof(struct in_addr)))
277       break;
278   if (!dest->h_addr_list[i])
279     return;
280
281   host_ip = inet_ntoa(remote.sin_addr);
282   if (!host_ip)
283     return;
284
285   *hostname = silc_calloc(strlen(host_name) + 1, sizeof(char));
286   memcpy(*hostname, host_name, strlen(host_name));
287   SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
288   *ip = silc_calloc(strlen(host_ip) + 1, sizeof(char));
289   memcpy(*ip, host_ip, strlen(host_ip));
290   SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
291 }
292
293 /* Return name of localhost. */
294
295 char *silc_net_localhost()
296 {
297   char hostname[256];
298   if (!gethostname(hostname, sizeof(hostname)))
299     return strdup(hostname);
300   return NULL;
301 }