updates.
[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 /* 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   int len = sizeof(server.sin_addr);
36
37   SILC_LOG_DEBUG(("Creating a new server listener"));
38
39   /* Create the socket */
40   sock = socket(AF_INET, SOCK_STREAM, 0);
41   if (sock < 0) {
42     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
43     return -1;
44   }
45
46   /* Set the socket options */
47   rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
48   if (rval < 0) {
49     SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno)));
50     return -1;
51   }
52
53   /* Set the socket information for bind() */
54   memset(&server, 0, sizeof(server));
55   server.sin_family = AF_INET;
56   if (port)
57     server.sin_port = htons(port);
58
59   /* Convert IP address to network byte order */
60   if (ip_addr)
61     silc_net_addr2bin(ip_addr, (unsigned char *)&server.sin_addr.s_addr, len);
62   else
63     server.sin_addr.s_addr = INADDR_ANY;
64
65   /* Bind the server socket */
66   rval = bind(sock, (struct sockaddr *)&server, sizeof(server));
67   if (rval < 0) {
68     SILC_LOG_ERROR(("Cannot bind socket: %s", strerror(errno)));
69     return -1;
70   }
71
72   /* Specify that we are listenning */
73   rval = listen(sock, 5);
74   if (rval < 0) {
75     SILC_LOG_ERROR(("Cannot set socket listenning: %s", strerror(errno)));
76     return -1;
77   }
78
79   /* Set the server socket to non-blocking mode */
80   silc_net_set_socket_nonblock(sock);
81
82   SILC_LOG_DEBUG(("Server listener created, fd=%d", sock));
83
84   return sock;
85 }
86
87 /* Closes the server by closing the socket connection. */
88
89 void silc_net_close_server(int sock)
90 {
91   shutdown(sock, 2);
92   close(sock);
93
94   SILC_LOG_DEBUG(("Server socket closed"));
95 }
96
97 /* Creates a connection (TCP/IP) to a remote host. Returns the connection
98    socket or -1 on error. This blocks the process while trying to create
99    the connection. */
100
101 int silc_net_create_connection(const char *local_ip, int port, 
102                                const char *host)
103 {
104   int sock, rval;
105   struct hostent *dest;
106   struct sockaddr_in desthost;
107
108   SILC_LOG_DEBUG(("Creating connection to host %s port %d", host, port));
109
110   /* Do host lookup */
111   dest = gethostbyname(host);
112   if (!dest) {
113     SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
114                     "IP address", host));
115     return -1;
116   }
117
118   /* Set socket information */
119   memset(&desthost, 0, sizeof(desthost));
120   desthost.sin_port = htons(port);
121   desthost.sin_family = AF_INET;
122   memcpy(&desthost.sin_addr, dest->h_addr_list[0], sizeof(desthost.sin_addr));
123
124   /* Create the connection socket */
125   sock = socket(AF_INET, SOCK_STREAM, 0);
126   if (sock < 0) {
127     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
128     return -1;
129   }
130
131   /* Bind to the local address if provided */
132   if (local_ip) {
133     struct sockaddr_in local;
134     int local_len = sizeof(local.sin_addr);
135
136     /* Set the socket information for bind() */
137     memset(&local, 0, sizeof(local));
138     local.sin_family = AF_INET;
139
140     /* Convert IP address to network byte order */
141     silc_net_addr2bin(local_ip, (unsigned char *)&local.sin_addr.s_addr, 
142                       local_len);
143
144     /* Bind the local socket */
145     rval = bind(sock, (struct sockaddr *)&local, sizeof(local));
146     if (rval < 0) {
147       SILC_LOG_ERROR(("Cannot connect to remote host: "
148                       "cannot bind socket: %s", strerror(errno)));
149       return -1;
150     }
151   }
152
153   /* Connect to the host */
154   rval = connect(sock, (struct sockaddr *)&desthost, sizeof(desthost));
155   if (rval < 0) {
156     SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
157     shutdown(sock, 2);
158     close(sock);
159     return -1;
160   }
161
162   /* Set appropriate options */
163 #if defined(TCP_NODELAY)
164   silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
165 #endif
166   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
167
168   SILC_LOG_DEBUG(("Connection created"));
169
170   return sock;
171 }
172
173 /* Creates a connection (TCP/IP) to a remote host. Returns the connection
174    socket or -1 on error. This creates non-blocking socket hence the
175    connection returns directly. To get the result of the connect() one
176    must select() the socket and read the result after it's ready. */
177
178 int silc_net_create_connection_async(const char *local_ip, int port, 
179                                      const char *host)
180 {
181   int sock, rval;
182   struct hostent *dest;
183   struct sockaddr_in desthost;
184
185   SILC_LOG_DEBUG(("Creating connection (async) to host %s port %d", 
186                   host, port));
187
188   /* Do host lookup */
189   dest = gethostbyname(host);
190   if (!dest) {
191     SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
192                     "IP address", host));
193     return -1;
194   }
195
196   /* Set socket information */
197   memset(&desthost, 0, sizeof(desthost));
198   desthost.sin_port = htons(port);
199   desthost.sin_family = AF_INET;
200   memcpy(&desthost.sin_addr, dest->h_addr_list[0], sizeof(desthost.sin_addr));
201
202   /* Create the connection socket */
203   sock = socket(AF_INET, SOCK_STREAM, 0);
204   if (sock < 0) {
205     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
206     return -1;
207   }
208
209   /* Bind to the local address if provided */
210   if (local_ip) {
211     struct sockaddr_in local;
212     int local_len = sizeof(local.sin_addr);
213
214     /* Set the socket information for bind() */
215     memset(&local, 0, sizeof(local));
216     local.sin_family = AF_INET;
217
218     /* Convert IP address to network byte order */
219     silc_net_addr2bin(local_ip, (unsigned char *)&local.sin_addr.s_addr, 
220                       local_len);
221
222     /* Bind the local socket */
223     rval = bind(sock, (struct sockaddr *)&local, sizeof(local));
224     if (rval < 0) {
225       SILC_LOG_ERROR(("Cannot connect to remote host: "
226                       "cannot bind socket: %s", strerror(errno)));
227       return -1;
228     }
229   }
230
231   /* Set the socket to non-blocking mode */
232   silc_net_set_socket_nonblock(sock);
233
234   /* Connect to the host */
235   rval = connect(sock, (struct sockaddr *)&desthost, sizeof(desthost));
236   if (rval < 0) {
237     if (errno !=  EINPROGRESS) {
238       SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
239       shutdown(sock, 2);
240       close(sock);
241       return -1;
242     }
243   }
244
245   /* Set appropriate options */
246 #if defined(TCP_NODELAY)
247   silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
248 #endif
249   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
250
251   SILC_LOG_DEBUG(("Connection operation in progress"));
252
253   return sock;
254 }
255
256 /* Closes the connection by closing the socket connection. */
257
258 void silc_net_close_connection(int sock)
259 {
260   close(sock);
261 }
262
263 /* Set's the socket to non-blocking mode. */
264
265 int silc_net_set_socket_nonblock(int sock)
266 {
267   return fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
268 }
269
270 /* Converts the IP number string from numbers-and-dots notation to
271    binary form. */
272
273 bool silc_net_addr2bin(const char *addr, unsigned char *bin,
274                        uint32 bin_len)
275 {
276   struct in_addr tmp;
277   int ret;
278
279   ret = inet_aton(addr, &tmp);
280
281   if (bin_len < 4)
282     return FALSE;
283
284   SILC_PUT32_LSB(tmp.s_addr, bin);
285
286   return ret != 0;
287 }
288
289 /* Converts the IP number string from numbers-and-dots notation to
290    binary form in network byte order. */
291
292 bool silc_net_addr2bin_ne(const char *addr, unsigned char *bin,
293                           uint32 bin_len)
294 {
295   struct in_addr tmp;
296   int ret;
297
298   ret = inet_aton(addr, &tmp);
299
300   if (bin_len < 4)
301     return FALSE;
302
303   SILC_PUT32_MSB(tmp.s_addr, bin);
304
305   return ret != 0;
306 }