1/*
2 *
3 *  BlueZ - Bluetooth protocol stack for Linux
4 *
5 *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
6 *  Copyright (C) 2002-2009  Marcel Holtmann <marcel@holtmann.org>
7 *
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 *  You should have received a copy of the GNU General Public License
20 *  along with this program; if not, write to the Free Software
21 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22 *
23 */
24
25#ifdef HAVE_CONFIG_H
26#include <config.h>
27#endif
28
29#include <stdio.h>
30#include <errno.h>
31#include <fcntl.h>
32#include <unistd.h>
33#include <stdint.h>
34#include <stdlib.h>
35#include <syslog.h>
36#include <setjmp.h>
37#include <string.h>
38
39#include "lib.h"
40#include "dund.h"
41
42#define MS_PPP      2
43#define MS_SUCCESS  1
44#define MS_FAILED  -1
45#define MS_TIMEOUT -2
46
47static sigjmp_buf jmp;
48static int        retry;
49static int        timeout;
50
51static void sig_alarm(int sig)
52{
53	siglongjmp(jmp, MS_TIMEOUT);
54}
55
56static int w4_str(int fd, char *str)
57{
58	char buf[40];
59	unsigned len = 0;
60	int r;
61
62	while (1) {
63		r = read(fd, buf + len, sizeof(buf) - len - 1);
64		if (r < 0) {
65			if (errno == EINTR || errno == EAGAIN)
66				continue;
67			break;
68		}
69		if (!r)
70			break;
71
72		len += r;
73
74		if (len < strlen(str))
75			continue;
76		buf[len] = 0;
77
78		if (strstr(buf, str))
79			return MS_SUCCESS;
80
81		/* Detect PPP */
82		if (strchr(buf, '~'))
83			return MS_PPP;
84	}
85	return MS_FAILED;
86}
87
88static int ms_server(int fd)
89{
90	switch (w4_str(fd, "CLIENT")) {
91	case MS_SUCCESS:
92		write_n(fd, "CLIENTSERVER", 12);
93	case MS_PPP:
94		return MS_SUCCESS;
95	default:
96		return MS_FAILED;
97	}
98}
99
100static int ms_client(int fd)
101{
102	write_n(fd, "CLIENT", 6);
103	return w4_str(fd, "CLIENTSERVER");
104}
105
106int ms_dun(int fd, int server, int timeo)
107{
108	sig_t osig;
109
110	retry    = 4;
111	timeout  = timeo;
112
113	if (!server)
114		timeout /= retry;
115
116	osig = signal(SIGALRM, sig_alarm);
117
118	while (1) {
119		int r = sigsetjmp(jmp, 1);
120		if (r) {
121			if (r == MS_TIMEOUT && !server && --retry)
122				continue;
123
124			alarm(0);
125			signal(SIGALRM, osig);
126
127			switch (r) {
128			case MS_SUCCESS:
129			case MS_PPP:
130				errno = 0;
131				return 0;
132
133			case MS_FAILED:
134				errno = EPROTO;
135				break;
136
137			case MS_TIMEOUT:
138				errno = ETIMEDOUT;
139				break;
140			}
141			return -1;
142		}
143
144		alarm(timeout);
145
146		if (server)
147			r = ms_server(fd);
148		else
149			r = ms_client(fd);
150
151		siglongjmp(jmp, r);
152	}
153}
154