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