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