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