Merged from silc_1_0_branch.
[silc.git] / lib / silcutil / unix / silcunixsockconn.c
1 /*
2
3   silcunixsockconn.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
24 /* Writes data from encrypted buffer to the socket connection. If the
25    data cannot be written at once, it will be written later with a timeout. 
26    The data is written from the data section of the buffer, not from head
27    or tail section. This automatically pulls the data section towards end
28    after writing the data. */
29
30 int silc_socket_write(SilcSocketConnection sock)
31 {
32   int ret = 0;
33   int fd = sock->sock;
34   SilcBuffer src = sock->outbuf;
35
36   if (!src)
37     return -2;
38   if (SILC_IS_DISABLED(sock))
39     return -1;
40
41   SILC_LOG_DEBUG(("Writing data to socket %d", fd));
42
43   if (src->len > 0) {
44     ret = write(fd, src->data, src->len);
45     if (ret < 0) {
46       if (errno == EAGAIN || errno == EINTR) {
47         SILC_LOG_DEBUG(("Could not write immediately, will do it later"));
48         return -2;
49       }
50       SILC_LOG_DEBUG(("Cannot write to socket: %s", strerror(errno)));
51       sock->sock_error = errno;
52       return -1;
53     }
54
55     if (ret < src->len) {
56       SILC_LOG_DEBUG(("Wrote data %d of %d bytes, will write rest later",
57                       ret, src->len));
58       silc_buffer_pull(src, ret);
59       return -2;
60     }
61
62     silc_buffer_pull(src, ret);
63   }
64
65   SILC_LOG_DEBUG(("Wrote data %d bytes", ret));
66
67   return ret;
68 }
69
70 /* QoS read handler, this will call the read and write events to indicate
71    that data is available again after a timeout. */
72
73 SILC_TASK_CALLBACK(silc_socket_read_qos)
74 {
75   SilcSocketConnection sock = context;
76   sock->qos->applied = TRUE;
77   silc_schedule_set_listen_fd(sock->qos->schedule, sock->sock,
78                               (SILC_TASK_READ | SILC_TASK_WRITE), TRUE);
79   sock->qos->applied = FALSE;
80   silc_socket_free(sock);
81 }
82
83 /* Reads data from the socket connection into the incoming data buffer.
84    It reads as much as possible from the socket connection. This returns
85    amount of bytes read or -1 on error or -2 on case where all of the
86    data could not be read at once. */
87
88 int silc_socket_read(SilcSocketConnection sock)
89 {
90   int len = 0;
91   unsigned char buf[SILC_SOCKET_READ_SIZE];
92   int fd = sock->sock;
93
94   if (SILC_IS_DISABLED(sock))
95     return -1;
96
97   /* If QoS was applied to socket then return earlier read data but apply
98      QoS to it too, if necessary. */
99   if (sock->qos) {
100     if (sock->qos->applied) {
101       if (sock->qos->data_len) {
102         /* Pull hidden data since we have it from earlier QoS apply */
103         silc_buffer_pull_tail(sock->inbuf, sock->qos->data_len);
104         len = sock->qos->data_len;
105         sock->qos->data_len = 0;
106       }
107
108       if (sock->inbuf->len - len > sock->qos->read_limit_bytes) {
109         /* Seems we need to apply QoS for the remaining data as well */
110         silc_schedule_task_add(sock->qos->schedule, sock->sock,
111                                silc_socket_read_qos, silc_socket_dup(sock),
112                                sock->qos->limit_sec, sock->qos->limit_usec,
113                                SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
114         silc_schedule_unset_listen_fd(sock->qos->schedule, sock->sock);
115       
116         /* Hide the rest of the data from the buffer. */
117         sock->qos->data_len = (sock->inbuf->len - len - 
118                                sock->qos->read_limit_bytes);
119         silc_buffer_push_tail(sock->inbuf, sock->qos->data_len);
120       }
121
122       if (sock->inbuf->len)
123         return sock->inbuf->len;
124     }
125
126     /* If we were called and we have active QoS data pending, return
127        with no data */
128     if (sock->qos->data_len) {
129       silc_schedule_unset_listen_fd(sock->qos->schedule, sock->sock);
130       return -2;
131     }
132   }
133
134   SILC_LOG_DEBUG(("Reading data from socket %d", fd));
135
136   /* Read the data from the socket. */
137   len = read(fd, buf, sizeof(buf));
138   if (len < 0) {
139     if (errno == EAGAIN || errno == EINTR) {
140       SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
141       return -2;
142     }
143     SILC_LOG_DEBUG(("Cannot read from socket: %d:%s", fd, strerror(errno)));
144     sock->sock_error = errno;
145     return -1;
146   }
147
148   if (!len)
149     return 0;
150
151   /* Insert the data to the buffer. */
152
153   if (!sock->inbuf)
154     sock->inbuf = silc_buffer_alloc(SILC_SOCKET_BUF_SIZE);
155   
156   /* If the data does not fit to the buffer reallocate it */
157   if ((sock->inbuf->end - sock->inbuf->tail) < len)
158     sock->inbuf = silc_buffer_realloc(sock->inbuf, sock->inbuf->truelen + 
159                                       (len * 2));
160   silc_buffer_put_tail(sock->inbuf, buf, len);
161   silc_buffer_pull_tail(sock->inbuf, len);
162
163   SILC_LOG_DEBUG(("Read %d bytes", len));
164
165   /* Apply QoS to the read data if necessary */
166   if (sock->qos) {
167     struct timeval curtime;
168     silc_gettimeofday(&curtime);
169
170     /* If we have passed the rate time limit, set our new time limit,
171        and zero the rate limit. */
172     if (!silc_compare_timeval(&curtime, &sock->qos->next_limit)) {
173       curtime.tv_sec++;
174       sock->qos->next_limit = curtime;
175       sock->qos->cur_rate = 0;
176     }
177     sock->qos->cur_rate++;
178
179     /* If we are not withing rate limit apply QoS for the read data */
180     if (sock->qos->cur_rate > sock->qos->read_rate) {
181       silc_schedule_task_add(sock->qos->schedule, sock->sock,
182                              silc_socket_read_qos, silc_socket_dup(sock),
183                              sock->qos->limit_sec, sock->qos->limit_usec,
184                              SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
185       silc_schedule_unset_listen_fd(sock->qos->schedule, sock->sock);
186
187       /* Check the byte limit as well, and do not return more than allowed */
188       if (sock->inbuf->len > sock->qos->read_limit_bytes) {
189         /* Hide the rest of the data from the buffer. */
190         sock->qos->data_len = sock->inbuf->len - sock->qos->read_limit_bytes;
191         silc_buffer_push_tail(sock->inbuf, sock->qos->data_len);
192         len = sock->inbuf->len;
193       } else {
194         /* Rate limit kicked in, do not return data yet */
195         return -2;
196       }
197     } else {
198       /* Check the byte limit, and do not return more than allowed */
199       if (sock->inbuf->len > sock->qos->read_limit_bytes) {
200         silc_schedule_task_add(sock->qos->schedule, sock->sock,
201                                silc_socket_read_qos, silc_socket_dup(sock),
202                                sock->qos->limit_sec, sock->qos->limit_usec,
203                                SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
204         silc_schedule_unset_listen_fd(sock->qos->schedule, sock->sock);
205
206         /* Hide the rest of the data from the buffer. */
207         sock->qos->data_len = sock->inbuf->len - sock->qos->read_limit_bytes;
208         silc_buffer_push_tail(sock->inbuf, sock->qos->data_len);
209         len = sock->inbuf->len;
210       }
211     }
212   }
213
214   return len;
215 }
216
217 /* Returns human readable socket error message */
218
219 bool silc_socket_get_error(SilcSocketConnection sock, char *error,
220                            SilcUInt32 error_len)
221 {
222   char *err;
223
224   if (!sock->sock_error)
225     return FALSE;
226
227   err = strerror(sock->sock_error);
228   if (strlen(err) > error_len)
229     return FALSE;
230
231   memset(error, 0, error_len);
232   memcpy(error, err, strlen(err));
233   return TRUE;
234 }