vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / kernel / network / devices / dialup / dialup.cpp
blob3a472d540ca504b0459fb287038536790c526508
1 /*
2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Philippe Houdoin, <phoudoin %at% haiku-os %dot% org>
7 */
10 #include <net_buffer.h>
11 #include <net_device.h>
12 #include <net_stack.h>
14 #include <KernelExport.h>
16 #include <errno.h>
17 #include <net/if.h>
18 #include <net/if_dl.h>
19 #include <net/if_media.h>
20 #include <net/if_types.h>
21 #include <new>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <termios.h>
26 #include <sys/uio.h>
29 #define HDLC_FLAG_SEQUENCE 0x7e
30 #define HDLC_CONTROL_ESCAPE 0x7d
32 #define HDLC_ALL_STATIONS 0xff
33 #define HDLC_UI 0x03
35 #define HDLC_HEADER_LENGTH 4
38 enum dialup_state {
39 DOWN,
40 DIALING,
41 UP,
42 HANGINGUP
45 struct dialup_device : net_device {
46 int fd;
47 struct termios line_config;
48 dialup_state state;
49 bigtime_t last_closing_flag_sequence_time;
50 bool data_mode;
51 char init_string[64];
52 char dial_string[64];
53 char escape_string[8];
54 bigtime_t escape_silence;
55 char hangup_string[16];
56 bigtime_t tx_flag_timeout;
57 uint32 rx_accm;
58 uint32 tx_accm[8];
61 net_buffer_module_info* gBufferModule;
62 static net_stack_module_info* sStackModule;
65 // #pragma mark -
68 static status_t
69 switch_to_command_mode(dialup_device* device)
71 if (device->state != UP)
72 return B_ERROR;
74 if (!device->data_mode)
75 return B_OK;
77 snooze(device->escape_silence);
79 ssize_t size = write(device->fd, device->escape_string,
80 strlen(device->escape_string));
81 if (size != (ssize_t)strlen(device->escape_string))
82 return B_IO_ERROR;
84 snooze(device->escape_silence);
85 device->data_mode = false;
86 return B_OK;
90 #if 0
91 static status_t
92 switch_to_data_mode(dialup_device* device)
94 if (device->state != UP)
95 return B_ERROR;
97 if (device->data_mode)
98 return B_OK;
100 // TODO: check if it's needed, as these days any
101 // escaped AT commands switch back to data mode automatically
102 // after their completion...
103 ssize_t size = write(device->fd, "ATO", 3);
104 if (size != 3)
105 return B_IO_ERROR;
107 device->data_mode = true;
108 return B_OK;
110 #endif
113 static status_t
114 send_command(dialup_device* device, const char* command)
116 status_t status;
117 if (device->data_mode) {
118 status = switch_to_command_mode(device);
119 if (status != B_OK)
120 return status;
123 ssize_t bytesWritten = write(device->fd, command, strlen(command));
124 if (bytesWritten != (ssize_t)strlen(command))
125 return B_IO_ERROR;
127 if (write(device->fd, "\r", 1) != 1)
128 return B_IO_ERROR;
130 return B_OK;
134 static status_t
135 read_command_reply(dialup_device* device, const char* command,
136 char* reply, int replyMaxSize)
138 if (device->data_mode)
139 return B_ERROR;
141 int i = 0;
142 while (i < replyMaxSize) {
144 ssize_t bytesRead = read(device->fd, &reply[i], 1);
145 if (bytesRead != 1)
146 return B_IO_ERROR;
148 if (reply[i] == '\n') {
149 // filter linefeed char
150 continue;
153 if (reply[i] == '\r') {
154 reply[i] = '\0';
156 // is command reply or command echo (if any) ?
157 if (!strcasecmp(reply, command))
158 return B_OK;
160 // It's command echo line. Just ignore it.
161 i = 0;
162 continue;
164 i++;
167 // replyMaxSize not large enough to store the full reply line.
168 return B_NO_MEMORY;
172 static status_t
173 hangup(dialup_device* device)
175 if (device->state != UP)
176 return B_ERROR;
178 // TODO: turn device's DTR down instead. Or do that too after sending command
179 char reply[8];
181 if (send_command(device, device->hangup_string) != B_OK
182 || read_command_reply(device, device->hangup_string,
183 reply, sizeof(reply)) != B_OK
184 || strcmp(reply, "OK"))
185 return B_ERROR;
187 device->state = DOWN;
188 return B_OK;
192 // #pragma mark -
195 status_t
196 dialup_init(const char* name, net_device** _device)
198 // make sure this is a device in /dev/ports
199 if (strncmp(name, "/dev/ports/", 11))
200 return B_BAD_VALUE;
202 status_t status = get_module(NET_BUFFER_MODULE_NAME, (module_info**)&gBufferModule);
203 if (status < B_OK)
204 return status;
206 dialup_device* device = new (std::nothrow)dialup_device;
207 if (device == NULL) {
208 put_module(NET_BUFFER_MODULE_NAME);
209 return B_NO_MEMORY;
212 memset(device, 0, sizeof(dialup_device));
214 strcpy(device->name, name);
215 device->flags = IFF_POINTOPOINT;
216 device->type = IFT_PPP; // this device handle RFC 1331 frame format only
217 device->mtu = 1500;
218 device->media = 0;
219 device->header_length = HDLC_HEADER_LENGTH;
221 device->fd = -1;
222 device->state = DOWN;
223 device->data_mode = false;
224 device->last_closing_flag_sequence_time = 0;
226 // default AT strings
227 strncpy(device->init_string, "ATZ", sizeof(device->init_string));
228 strncpy(device->dial_string, "ATDT", sizeof(device->dial_string));
229 strncpy(device->hangup_string, "ATH0", sizeof(device->hangup_string));
231 strncpy(device->escape_string, "+++", sizeof(device->escape_string));
232 device->escape_silence = 1000000;
234 device->tx_flag_timeout = 1000000;
236 // default rx & tx Async-Control-Character-Map
237 memset(&device->rx_accm, 0xFF, sizeof(device->rx_accm));
238 memset(&device->tx_accm, 0xFF, sizeof(device->tx_accm));
240 *_device = device;
241 return B_OK;
245 status_t
246 dialup_uninit(net_device* _device)
248 dialup_device* device = (dialup_device*)_device;
249 delete device;
251 put_module(NET_BUFFER_MODULE_NAME);
252 gBufferModule = NULL;
253 return B_OK;
257 status_t
258 dialup_up(net_device* _device)
260 dialup_device* device = (dialup_device*)_device;
262 device->fd = open(device->name, O_RDWR);
263 if (device->fd < 0)
264 return errno;
266 device->media = IFM_ACTIVE;
268 // init port
269 if (ioctl(device->fd, TCGETA, &device->line_config,
270 sizeof(device->line_config)) < 0)
271 goto err;
273 // adjust options
274 device->line_config.c_cflag &= ~CBAUD;
275 device->line_config.c_cflag &= CSIZE;
276 device->line_config.c_cflag &= CS8;
277 device->line_config.c_cflag |= B115200; // TODO: make this configurable too...
278 device->line_config.c_cflag |= (CLOCAL | CREAD);
279 device->line_config.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
280 device->line_config.c_oflag &= ~OPOST;
281 device->line_config.c_cc[VMIN] = 0;
282 device->line_config.c_cc[VTIME] = 10;
284 // set new options
285 if(ioctl(device->fd, TCSETA, &device->line_config,
286 sizeof(device->line_config)) < 0)
287 goto err;
289 // init modem & start dialing phase
291 char reply[32];
293 if (strlen(device->init_string) > 0) {
294 // Send modem init string
295 if (send_command(device, device->init_string) != B_OK
296 || read_command_reply(device, device->init_string,
297 reply, sizeof(reply)) != B_OK
298 || strcmp(reply, "OK")) {
299 errno = B_IO_ERROR;
300 goto err;
304 reply[0] = '\0';
306 if (strlen(device->dial_string) > 0) {
307 // Send dialing string
308 device->state = DIALING;
309 if (send_command(device, device->dial_string) != B_OK
310 || read_command_reply(device, device->dial_string,
311 reply, sizeof(reply)) != B_OK
312 || strncmp(reply, "CONNECT", 7)) {
313 errno = B_IO_ERROR;
314 goto err;
318 device->state = UP;
319 device->data_mode = true;
321 device->media |= IFM_FULL_DUPLEX;
322 device->flags |= IFF_LINK;
324 device->link_quality = 1000;
325 if (strlen(reply) > 7) {
326 // get speed from "CONNECTxxxx" reply
327 device->link_speed = atoi(&reply[8]);
328 } else {
329 // Set default speed (theorically, it could be 300 bits/s even)
330 device->link_speed = 19200;
333 return B_OK;
335 err:
336 close(device->fd);
337 device->fd = -1;
338 device->media = 0;
340 return errno;
344 void
345 dialup_down(net_device* _device)
347 dialup_device* device = (dialup_device*)_device;
349 if (device->flags & IFF_LINK
350 && hangup(device) == B_OK)
351 device->flags &= ~IFF_LINK;
353 close(device->fd);
354 device->fd = -1;
355 device->media = 0;
359 status_t
360 dialup_control(net_device* _device, int32 op, void* argument,
361 size_t length)
363 dialup_device* device = (dialup_device*)_device;
364 return ioctl(device->fd, op, argument, length);
368 status_t
369 dialup_send_data(net_device* _device, net_buffer* buffer)
371 dialup_device* device = (dialup_device*)_device;
373 if (device->fd == -1)
374 return B_FILE_ERROR;
376 dprintf("try to send HDLC packet of %" B_PRIu32 " bytes "
377 "(flags 0x%" B_PRIx32 "):\n", buffer->size, buffer->flags);
379 if (buffer->size < HDLC_HEADER_LENGTH)
380 return B_BAD_VALUE;
382 iovec* ioVectors = NULL;
383 iovec* ioVector;
384 uint8* packet = NULL;
385 int packetSize = 0;
386 status_t status;
387 ssize_t bytesWritten;
389 uint32 vectorCount = gBufferModule->count_iovecs(buffer);
390 if (vectorCount < 1) {
391 status = B_BAD_VALUE;
392 goto err;
395 ioVectors = (iovec*)malloc(sizeof(iovec)*vectorCount);
396 if (ioVectors == NULL) {
397 status = B_NO_MEMORY;
398 goto err;
400 gBufferModule->get_iovecs(buffer, ioVectors, vectorCount);
402 // encode HDLC packet
404 // worst case: begin and end sequence flags plus each payload byte escaped
405 packet = (uint8*)malloc(2 + 2 * buffer->size);
406 if (packet == NULL) {
407 status = B_NO_MEMORY;
408 goto err;
411 // Mark frame start if the prior frame closing flag was sent
412 // more than a second ago.
413 // Otherwise, the prior closing flag sequence is the open flag of this
414 // frame
415 if (device->tx_flag_timeout
416 && system_time() - device->last_closing_flag_sequence_time
417 > device->tx_flag_timeout) {
418 packet[packetSize++] = HDLC_FLAG_SEQUENCE;
421 // encode frame data
422 ioVector = ioVectors;
423 while (vectorCount--) {
424 uint8* data = (uint8*) ioVector->iov_base;
425 for (unsigned int i = 0; i < ioVector->iov_len; i++) {
426 if (data[i] < 0x20
427 || data[i] == HDLC_FLAG_SEQUENCE
428 || data[i] == HDLC_CONTROL_ESCAPE) {
429 // needs escape
430 packet[packetSize++] = HDLC_CONTROL_ESCAPE;
431 packet[packetSize++] = data[i] ^ 0x20;
432 } else
433 packet[packetSize++] = data[i];
435 // next io vector
436 ioVector++;
439 // mark frame end
440 packet[packetSize++] = HDLC_FLAG_SEQUENCE;
442 // send HDLC packet
444 bytesWritten = write(device->fd, packet, packetSize);
445 if (bytesWritten < 0) {
446 status = errno;
447 goto err;
449 device->last_closing_flag_sequence_time = system_time();
451 device->stats.send.packets++;
452 device->stats.send.bytes += bytesWritten;
453 status = B_OK;
454 goto done;
456 err:
457 device->stats.send.errors++;
459 done:
460 free(ioVectors);
461 free(packet);
462 return status;
466 status_t
467 dialup_receive_data(net_device* _device, net_buffer** _buffer)
469 dialup_device* device = (dialup_device*)_device;
471 if (device->fd == -1)
472 return B_FILE_ERROR;
474 net_buffer* buffer = gBufferModule->create(256);
475 if (buffer == NULL)
476 return ENOBUFS;
478 status_t status;
479 ssize_t bytesRead;
480 uint8* data = NULL;
481 uint8* packet = (uint8*)malloc(2 + 2 * buffer->size);
482 if (packet == NULL) {
483 status = B_NO_MEMORY;
484 goto err;
487 status = gBufferModule->append_size(buffer,
488 device->mtu + HDLC_HEADER_LENGTH, (void**)&data);
489 if (status == B_OK && data == NULL) {
490 dprintf("scattered I/O is not yet supported by dialup device.\n");
491 status = B_NOT_SUPPORTED;
493 if (status < B_OK)
494 goto err;
496 while (true) {
497 bytesRead = read(device->fd, data, device->mtu + HDLC_HEADER_LENGTH);
498 if (bytesRead < 0) {
499 // TODO
503 status = gBufferModule->trim(buffer, bytesRead);
504 if (status < B_OK) {
505 device->stats.receive.dropped++;
506 goto err;
509 device->stats.receive.bytes += bytesRead;
510 device->stats.receive.packets++;
512 *_buffer = buffer;
513 status = B_OK;
514 goto done;
516 err:
517 gBufferModule->free(buffer);
518 device->stats.receive.errors++;
520 done:
521 free(packet);
522 return status;
526 status_t
527 dialup_set_mtu(net_device* _device, size_t mtu)
529 dialup_device* device = (dialup_device*)_device;
531 device->mtu = mtu;
532 return B_OK;
536 status_t
537 dialup_set_promiscuous(net_device* _device, bool promiscuous)
539 return B_NOT_SUPPORTED;
543 status_t
544 dialup_set_media(net_device* device, uint32 media)
546 return B_NOT_SUPPORTED;
550 status_t
551 dialup_add_multicast(struct net_device* _device, const sockaddr* _address)
553 return B_NOT_SUPPORTED;
557 status_t
558 dialup_remove_multicast(struct net_device* _device, const sockaddr* _address)
560 return B_NOT_SUPPORTED;
564 static status_t
565 dialup_std_ops(int32 op, ...)
567 switch (op) {
568 case B_MODULE_INIT:
570 status_t status = get_module(NET_STACK_MODULE_NAME,
571 (module_info**)&sStackModule);
572 if (status < B_OK)
573 return status;
575 return B_OK;
578 case B_MODULE_UNINIT:
580 put_module(NET_STACK_MODULE_NAME);
581 return B_OK;
584 default:
585 return B_ERROR;
590 net_device_module_info sDialUpModule = {
592 "network/devices/dialup/v1",
594 dialup_std_ops
596 dialup_init,
597 dialup_uninit,
598 dialup_up,
599 dialup_down,
600 dialup_control,
601 dialup_send_data,
602 dialup_receive_data,
603 dialup_set_mtu,
604 dialup_set_promiscuous,
605 dialup_set_media,
606 dialup_add_multicast,
607 dialup_remove_multicast,
610 module_info* modules[] = {
611 (module_info*)&sDialUpModule,
612 NULL