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(PF_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 = PF_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(int port, char *host)
102 {
103   int sock, rval;
104   struct hostent *dest;
105   struct sockaddr_in desthost;
106
107   SILC_LOG_DEBUG(("Creating connection to host %s port %d", host, port));
108
109   /* Do host lookup */
110   dest = gethostbyname(host);
111   if (!dest) {
112     SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
113                     "IP address", host));
114     return -1;
115   }
116
117   /* Set socket information */
118   memset(&desthost, 0, sizeof(desthost));
119   desthost.sin_port = htons(port);
120   desthost.sin_family = PF_INET;
121   memcpy(&desthost.sin_addr, dest->h_addr_list[0], sizeof(desthost.sin_addr));
122
123   /* Create the connection socket */
124   sock = socket(PF_INET, SOCK_STREAM, 0);
125   if (sock < 0) {
126     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
127     return -1;
128   }
129
130   /* Connect to the host */
131   rval = connect(sock, (struct sockaddr *)&desthost, sizeof(desthost));
132   if (rval < 0) {
133     SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
134     shutdown(sock, 2);
135     close(sock);
136     return -1;
137   }
138
139   /* Set appropriate options */
140 #if defined(TCP_NODELAY)
141   silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
142 #endif
143   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
144
145   SILC_LOG_DEBUG(("Connection created"));
146
147   return sock;
148 }
149
150 /* Creates a connection (TCP/IP) to a remote host. Returns the connection
151    socket or -1 on error. This creates non-blocking socket hence the
152    connection returns directly. To get the result of the connect() one
153    must select() the socket and read the result after it's ready. */
154
155 int silc_net_create_connection_async(int port, char *host)
156 {
157   int sock, rval;
158   struct hostent *dest;
159   struct sockaddr_in desthost;
160
161   SILC_LOG_DEBUG(("Creating connection (async) to host %s port %d", 
162                   host, port));
163
164   /* Do host lookup */
165   dest = gethostbyname(host);
166   if (!dest) {
167     SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
168                     "IP address", host));
169     return -1;
170   }
171
172   /* Set socket information */
173   memset(&desthost, 0, sizeof(desthost));
174   desthost.sin_port = htons(port);
175   desthost.sin_family = PF_INET;
176   memcpy(&desthost.sin_addr, dest->h_addr_list[0], sizeof(desthost.sin_addr));
177
178   /* Create the connection socket */
179   sock = socket(PF_INET, SOCK_STREAM, 0);
180   if (sock < 0) {
181     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
182     return -1;
183   }
184
185   /* Set the socket to non-blocking mode */
186   silc_net_set_socket_nonblock(sock);
187
188   /* Connect to the host */
189   rval = connect(sock, (struct sockaddr *)&desthost, sizeof(desthost));
190   if (rval < 0) {
191     if (errno !=  EINPROGRESS) {
192       SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
193       shutdown(sock, 2);
194       close(sock);
195       return -1;
196     }
197   }
198
199   /* Set appropriate options */
200 #if defined(TCP_NODELAY)
201   silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
202 #endif
203   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
204
205   SILC_LOG_DEBUG(("Connection operation in progress"));
206
207   return sock;
208 }
209
210 /* Closes the connection by closing the socket connection. */
211
212 void silc_net_close_connection(int sock)
213 {
214   close(sock);
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 /* Converts the IP number string from numbers-and-dots notation to
225    binary form. */
226
227 bool silc_net_addr2bin(const char *addr, unsigned char *bin,
228                        uint32 bin_len)
229 {
230   struct in_addr tmp;
231   int ret;
232
233   ret = inet_aton(addr, &tmp);
234
235   if (bin_len < 4)
236     return FALSE;
237
238   SILC_PUT32_LSB(tmp.s_addr, bin);
239
240   return ret != 0;
241 }