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 int silc_net_set_socket_nonblock(int sock);
26
27 /* This function creates server or daemon or listener or what ever. This
28    does not fork a new process, it must be done by the caller if caller
29    wants to create a child process. This is used by the SILC server. 
30    If argument `ip_addr' is NULL `any' address will be used. Returns 
31    the created socket or -1 on error. */
32
33 int silc_net_create_server(int port, char *ip_addr)
34 {
35   int sock, rval;
36   struct sockaddr_in server;
37   int len = sizeof(server.sin_addr);
38
39   SILC_LOG_DEBUG(("Creating a new server listener"));
40
41   /* Create the socket */
42   sock = socket(PF_INET, SOCK_STREAM, 0);
43   if (sock < 0) {
44     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
45     return -1;
46   }
47
48   /* Set the socket options */
49   rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
50   if (rval < 0) {
51     SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno)));
52     return -1;
53   }
54
55   /* Set the socket information for bind() */
56   memset(&server, 0, sizeof(server));
57   server.sin_family = PF_INET;
58   if (port)
59     server.sin_port = htons(port);
60
61   /* Convert IP address to network byte order */
62   if (ip_addr)
63     silc_net_addr2bin(ip_addr, (unsigned char *)&server.sin_addr.s_addr, len);
64   else
65     server.sin_addr.s_addr = INADDR_ANY;
66
67   /* Bind the server socket */
68   rval = bind(sock, (struct sockaddr *)&server, sizeof(server));
69   if (rval < 0) {
70     SILC_LOG_ERROR(("Cannot bind socket: %s", strerror(errno)));
71     return -1;
72   }
73
74   /* Specify that we are listenning */
75   rval = listen(sock, 5);
76   if (rval < 0) {
77     SILC_LOG_ERROR(("Cannot set socket listenning: %s", strerror(errno)));
78     return -1;
79   }
80
81   /* Set the server socket to non-blocking mode */
82   silc_net_set_socket_nonblock(sock);
83
84   SILC_LOG_DEBUG(("Server listener created, fd=%d", sock));
85
86   return sock;
87 }
88
89 /* Closes the server by closing the socket connection. */
90
91 void silc_net_close_server(int sock)
92 {
93   shutdown(sock, 2);
94   close(sock);
95
96   SILC_LOG_DEBUG(("Server socket closed"));
97 }
98
99 /* Creates a connection (TCP/IP) to a remote host. Returns the connection
100    socket or -1 on error. This blocks the process while trying to create
101    the connection. */
102
103 int silc_net_create_connection(int port, 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", 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 = PF_INET;
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 options */
141   silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
142   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
143
144   SILC_LOG_DEBUG(("Connection created"));
145
146   return sock;
147 }
148
149 /* Creates a connection (TCP/IP) to a remote host. Returns the connection
150    socket or -1 on error. This creates non-blocking socket hence the
151    connection returns directly. To get the result of the connect() one
152    must select() the socket and read the result after it's ready. */
153
154 int silc_net_create_connection_async(int port, char *host)
155 {
156   int sock, rval;
157   struct hostent *dest;
158   struct sockaddr_in desthost;
159
160   SILC_LOG_DEBUG(("Creating connection (async) to host %s port %d", 
161                   host, port));
162
163   /* Do host lookup */
164   dest = gethostbyname(host);
165   if (!dest) {
166     SILC_LOG_ERROR(("Network (%s) unreachable", host));
167     return -1;
168   }
169
170   /* Set socket information */
171   memset(&desthost, 0, sizeof(desthost));
172   desthost.sin_port = htons(port);
173   desthost.sin_family = PF_INET;
174   memcpy(&desthost.sin_addr, dest->h_addr_list[0], sizeof(desthost.sin_addr));
175
176   /* Create the connection socket */
177   sock = socket(PF_INET, SOCK_STREAM, 0);
178   if (sock < 0) {
179     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
180     return -1;
181   }
182
183   /* Set the socket to non-blocking mode */
184   silc_net_set_socket_nonblock(sock);
185
186   /* Connect to the host */
187   rval = connect(sock, (struct sockaddr *)&desthost, sizeof(desthost));
188   if (rval < 0) {
189     if (errno !=  EINPROGRESS) {
190       SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
191       shutdown(sock, 2);
192       close(sock);
193       return -1;
194     }
195   }
196
197   /* Set appropriate options */
198   silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
199   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
200
201   SILC_LOG_DEBUG(("Connection operation in progress"));
202
203   return sock;
204 }
205
206 /* Closes the connection by closing the socket connection. */
207
208 void silc_net_close_connection(int sock)
209 {
210   close(sock);
211 }
212
213 /* Set's the socket to non-blocking mode. */
214
215 int silc_net_set_socket_nonblock(int sock)
216 {
217   return fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
218 }
219
220 /* Converts the IP number string from numbers-and-dots notation to
221    binary form. */
222
223 bool silc_net_addr2bin(const char *addr, unsigned char *bin,
224                        uint32 bin_len)
225 {
226   struct in_addr tmp;
227   int ret;
228
229   ret = inet_aton(addr, &tmp);
230
231   if (bin_len < 4)
232     return FALSE;
233
234   SILC_PUT32_LSB(tmp.s_addr, bin);
235
236   return ret != 0;
237 }