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