New silcconfig library and server parser. Merged silc-newconfig-final.patch.
[silc.git] / lib / silcutil / unix / silcunixnet.c
1 /*
2
3   silcunixnet.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2001 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 #ifdef HAVE_IPV6
26 #define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ?    \
27   sizeof(so.sin6) : sizeof(so.sin))
28 #else
29 #define SIZEOF_SOCKADDR(so) (sizeof(so.sin))
30 #endif
31
32 typedef union {
33   struct sockaddr sa;
34   struct sockaddr_in sin;
35 #ifdef HAVE_IPV6
36   struct sockaddr_in6 sin6;
37 #endif
38 } SilcSockaddr;
39
40 static bool silc_net_set_sockaddr(SilcSockaddr *addr, const char *ip_addr,
41                                   int port)
42 {
43   int len;
44
45   memset(addr, 0, sizeof(*addr));
46
47   /* Check for IPv4 and IPv6 addresses */
48   if (ip_addr) {
49     if (!silc_net_is_ip(ip_addr)) {
50       SILC_LOG_ERROR(("%s is not IP address", ip_addr));
51       return FALSE;
52     }
53
54     if (silc_net_is_ip4(ip_addr)) {
55       /* IPv4 address */
56       len = sizeof(addr->sin.sin_addr);
57       silc_net_addr2bin(ip_addr, 
58                         (unsigned char *)&addr->sin.sin_addr.s_addr, len);
59       addr->sin.sin_family = AF_INET;
60       addr->sin.sin_port = port ? htons(port) : 0;
61     } else {
62 #ifdef HAVE_IPV6
63       /* IPv6 address */
64       len = sizeof(addr->sin6.sin6_addr);
65       silc_net_addr2bin(ip_addr, 
66                         (unsigned char *)&addr->sin6.sin6_addr, len);
67       addr->sin6.sin6_family = AF_INET6;
68       addr->sin6.sin6_port = port ? htons(port) : 0;
69 #else
70       SILC_LOG_ERROR(("IPv6 support is not compiled in"));
71       return FALSE;
72 #endif
73     }
74   } else {
75     /* Any address */
76     addr->sin.sin_family = AF_INET;
77     addr->sin.sin_addr.s_addr = INADDR_ANY;
78     if (port)
79       addr->sin.sin_port = htons(port);
80   }
81
82   return TRUE;
83 }
84
85 /* This function creates server or daemon or listener or what ever. This
86    does not fork a new process, it must be done by the caller if caller
87    wants to create a child process. This is used by the SILC server. 
88    If argument `ip_addr' is NULL `any' address will be used. Returns 
89    the created socket or -1 on error. */
90
91 int silc_net_create_server(int port, const char *ip_addr)
92 {
93   int sock, rval;
94   SilcSockaddr server;
95
96   SILC_LOG_DEBUG(("Creating a new server listener"));
97
98   /* Set sockaddr for server */
99   if (!silc_net_set_sockaddr(&server, ip_addr, port))
100     return -1;
101
102   /* Create the socket */
103   sock = socket(server.sin.sin_family, SOCK_STREAM, 0);
104   if (sock < 0) {
105     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
106     return -1;
107   }
108
109   /* Set the socket options */
110   rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
111   if (rval < 0) {
112     SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno)));
113     return -1;
114   }
115
116   /* Bind the server socket */
117   rval = bind(sock, &server.sa, SIZEOF_SOCKADDR(server));
118   if (rval < 0) {
119     SILC_LOG_DEBUG(("Cannot bind socket: %s", strerror(errno)));
120     return -1;
121   }
122
123   /* Specify that we are listenning */
124   rval = listen(sock, 5);
125   if (rval < 0) {
126     SILC_LOG_ERROR(("Cannot set socket listenning: %s", strerror(errno)));
127     return -1;
128   }
129
130   /* Set the server socket to non-blocking mode */
131   silc_net_set_socket_nonblock(sock);
132
133   SILC_LOG_DEBUG(("Server listener created, fd=%d", sock));
134
135   return sock;
136 }
137
138 /* Closes the server by closing the socket connection. */
139
140 void silc_net_close_server(int sock)
141 {
142   shutdown(sock, 2);
143   close(sock);
144
145   SILC_LOG_DEBUG(("Server socket closed"));
146 }
147
148 /* Creates a connection (TCP/IP) to a remote host. Returns the connection
149    socket or -1 on error. This blocks the process while trying to create
150    the connection. */
151
152 int silc_net_create_connection(const char *local_ip, int port, 
153                                const char *host)
154 {
155   int sock, rval;
156   char ip_addr[64];
157   SilcSockaddr desthost;
158
159   SILC_LOG_DEBUG(("Creating connection to host %s port %d", host, port));
160
161   /* Do host lookup */
162   if (!silc_net_gethostbyname(host, ip_addr, sizeof(ip_addr))) {
163     SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
164                     "IP address", host));
165     return -1;
166   }
167
168   /* Set sockaddr for this connection */
169   if (!silc_net_set_sockaddr(&desthost, ip_addr, port))
170     return -1;
171
172   /* Create the connection socket */
173   sock = socket(desthost.sin.sin_family, SOCK_STREAM, 0);
174   if (sock < 0) {
175     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
176     return -1;
177   }
178
179   /* Bind to the local address if provided */
180   if (local_ip) {
181     SilcSockaddr local;
182
183     /* Set sockaddr for local listener, and try to bind it. */
184     if (silc_net_set_sockaddr(&local, local_ip, 0))
185       bind(sock, &local.sa, sizeof(local));
186   }
187
188   /* Connect to the host */
189   rval = connect(sock, &desthost.sa, sizeof(desthost));
190   if (rval < 0) {
191     SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
192     shutdown(sock, 2);
193     close(sock);
194     return -1;
195   }
196
197   /* Set appropriate options */
198 #if defined(TCP_NODELAY)
199   silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
200 #endif
201   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
202
203   SILC_LOG_DEBUG(("Connection created"));
204
205   return sock;
206 }
207
208 /* Creates a connection (TCP/IP) to a remote host. Returns the connection
209    socket or -1 on error. This creates non-blocking socket hence the
210    connection returns directly. To get the result of the connect() one
211    must select() the socket and read the result after it's ready. */
212
213 int silc_net_create_connection_async(const char *local_ip, int port, 
214                                      const char *host)
215 {
216   int sock, rval;
217   char ip_addr[64];
218   SilcSockaddr desthost;
219
220   SILC_LOG_DEBUG(("Creating connection (async) to host %s port %d", 
221                   host, port));
222
223   /* Do host lookup */
224   if (!silc_net_gethostbyname(host, ip_addr, sizeof(ip_addr))) {
225     SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
226                     "IP address", host));
227     return -1;
228   }
229
230   /* Set sockaddr for this connection */
231   if (!silc_net_set_sockaddr(&desthost, ip_addr, port))
232     return -1;
233
234   /* Create the connection socket */
235   sock = socket(desthost.sin.sin_family, SOCK_STREAM, 0);
236   if (sock < 0) {
237     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
238     return -1;
239   }
240
241   /* Bind to the local address if provided */
242   if (local_ip) {
243     SilcSockaddr local;
244
245     /* Set sockaddr for local listener, and try to bind it. */
246     if (silc_net_set_sockaddr(&local, local_ip, 0))
247       bind(sock, &local.sa, sizeof(local));
248   }
249
250   /* Set the socket to non-blocking mode */
251   silc_net_set_socket_nonblock(sock);
252
253   /* Connect to the host */
254   rval = connect(sock, &desthost.sa, sizeof(desthost));
255   if (rval < 0) {
256     if (errno !=  EINPROGRESS) {
257       SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
258       shutdown(sock, 2);
259       close(sock);
260       return -1;
261     }
262   }
263
264   /* Set appropriate options */
265 #if defined(TCP_NODELAY)
266   silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
267 #endif
268   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
269
270   SILC_LOG_DEBUG(("Connection operation in progress"));
271
272   return sock;
273 }
274
275 /* Closes the connection by closing the socket connection. */
276
277 void silc_net_close_connection(int sock)
278 {
279   close(sock);
280 }
281
282 /* Set's the socket to non-blocking mode. */
283
284 int silc_net_set_socket_nonblock(int sock)
285 {
286   return fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
287 }
288
289 /* Converts the IP number string from numbers-and-dots notation to
290    binary form. */
291
292 bool silc_net_addr2bin(const char *addr, void *bin, uint32 bin_len)
293 {
294   int ret = 0;
295
296   if (silc_net_is_ip4(addr)) {
297     /* IPv4 address */
298     struct in_addr tmp;
299     ret = inet_aton(addr, &tmp);
300     if (bin_len < 4)
301       return FALSE;
302     
303     memcpy(bin, (unsigned char *)&tmp.s_addr, 4);
304 #ifdef HAVE_IPV6
305   } else {
306     /* IPv6 address */
307     if (bin_len < 16)
308       return FALSE;
309
310     ret = inet_pton(AF_INET6, addr, &bin);
311 #endif /* HAVE_IPV6 */
312   }
313
314   return ret != 0;
315 }