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