1/*
2 * cbcp - Call Back Configuration Protocol.
3 *
4 * Copyright (c) 1995 Pedro Roque Marques
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms are permitted
8 * provided that the above copyright notice and this paragraph are
9 * duplicated in all such forms and that any documentation,
10 * advertising materials, and other materials related to such
11 * distribution and use acknowledge that the software was developed
12 * by Pedro Roque Marques.  The name of the author may not be used to
13 * endorse or promote products derived from this software without
14 * specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 */
20
21#define RCSID	"$Id$"
22
23#include <stdio.h>
24#include <string.h>
25#include <sys/types.h>
26#include <sys/time.h>
27
28#include "pppd.h"
29#include "cbcp.h"
30#include "fsm.h"
31#include "lcp.h"
32
33static const char rcsid[] = RCSID;
34
35/*
36 * Options.
37 */
38static int setcbcp __P((char **));
39
40static option_t cbcp_option_list[] = {
41    { "callback", o_special, setcbcp,
42      "Ask for callback", OPT_PRIO | OPT_A2STRVAL, &cbcp[0].us_number },
43    { NULL }
44};
45
46/*
47 * Protocol entry points.
48 */
49static void cbcp_init      __P((int unit));
50static void cbcp_open      __P((int unit));
51static void cbcp_lowerup   __P((int unit));
52static void cbcp_input     __P((int unit, u_char *pkt, int len));
53static void cbcp_protrej   __P((int unit));
54static int  cbcp_printpkt  __P((u_char *pkt, int len,
55				void (*printer) __P((void *, char *, ...)),
56				void *arg));
57
58struct protent cbcp_protent = {
59    PPP_CBCP,
60    cbcp_init,
61    cbcp_input,
62    cbcp_protrej,
63    cbcp_lowerup,
64    NULL,
65    cbcp_open,
66    NULL,
67    cbcp_printpkt,
68    NULL,
69    0,
70    "CBCP",
71    NULL,
72    cbcp_option_list,
73    NULL,
74    NULL,
75    NULL
76};
77
78cbcp_state cbcp[NUM_PPP];
79
80/* internal prototypes */
81
82static void cbcp_recvreq __P((cbcp_state *us, char *pckt, int len));
83static void cbcp_resp __P((cbcp_state *us));
84static void cbcp_up __P((cbcp_state *us));
85static void cbcp_recvack __P((cbcp_state *us, char *pckt, int len));
86static void cbcp_send __P((cbcp_state *us, u_char code, u_char *buf, int len));
87
88/* option processing */
89static int
90setcbcp(argv)
91    char **argv;
92{
93    lcp_wantoptions[0].neg_cbcp = 1;
94    cbcp_protent.enabled_flag = 1;
95    cbcp[0].us_number = strdup(*argv);
96    if (cbcp[0].us_number == 0)
97	novm("callback number");
98    cbcp[0].us_type |= (1 << CB_CONF_USER);
99    cbcp[0].us_type |= (1 << CB_CONF_ADMIN);
100    return (1);
101}
102
103/* init state */
104static void
105cbcp_init(iface)
106    int iface;
107{
108    cbcp_state *us;
109
110    us = &cbcp[iface];
111    memset(us, 0, sizeof(cbcp_state));
112    us->us_unit = iface;
113    us->us_type |= (1 << CB_CONF_NO);
114}
115
116/* lower layer is up */
117static void
118cbcp_lowerup(iface)
119    int iface;
120{
121    cbcp_state *us = &cbcp[iface];
122
123    dbglog("cbcp_lowerup");
124    dbglog("want: %d", us->us_type);
125
126    if (us->us_type == CB_CONF_USER)
127        dbglog("phone no: %s", us->us_number);
128}
129
130static void
131cbcp_open(unit)
132    int unit;
133{
134    dbglog("cbcp_open");
135}
136
137/* process an incomming packet */
138static void
139cbcp_input(unit, inpacket, pktlen)
140    int unit;
141    u_char *inpacket;
142    int pktlen;
143{
144    u_char *inp;
145    u_char code, id;
146    u_short len;
147
148    cbcp_state *us = &cbcp[unit];
149
150    inp = inpacket;
151
152    if (pktlen < CBCP_MINLEN) {
153        error("CBCP packet is too small");
154	return;
155    }
156
157    GETCHAR(code, inp);
158    GETCHAR(id, inp);
159    GETSHORT(len, inp);
160
161
162    len -= CBCP_MINLEN;
163
164    switch(code) {
165    case CBCP_REQ:
166        us->us_id = id;
167	cbcp_recvreq(us, inp, len);
168	break;
169
170    case CBCP_RESP:
171	dbglog("CBCP_RESP received");
172	break;
173
174    case CBCP_ACK:
175	if (id != us->us_id)
176	    dbglog("id doesn't match: expected %d recv %d",
177		   us->us_id, id);
178
179	cbcp_recvack(us, inp, len);
180	break;
181
182    default:
183	break;
184    }
185}
186
187/* protocol was rejected by foe */
188void cbcp_protrej(int iface)
189{
190}
191
192char *cbcp_codenames[] = {
193    "Request", "Response", "Ack"
194};
195
196char *cbcp_optionnames[] = {
197    "NoCallback",
198    "UserDefined",
199    "AdminDefined",
200    "List"
201};
202
203/* pretty print a packet */
204static int
205cbcp_printpkt(p, plen, printer, arg)
206    u_char *p;
207    int plen;
208    void (*printer) __P((void *, char *, ...));
209    void *arg;
210{
211    int code, opt, id, len, olen, delay;
212    u_char *pstart;
213
214    if (plen < HEADERLEN)
215	return 0;
216    pstart = p;
217    GETCHAR(code, p);
218    GETCHAR(id, p);
219    GETSHORT(len, p);
220    if (len < HEADERLEN || len > plen)
221	return 0;
222
223    if (code >= 1 && code <= sizeof(cbcp_codenames) / sizeof(char *))
224	printer(arg, " %s", cbcp_codenames[code-1]);
225    else
226	printer(arg, " code=0x%x", code);
227
228    printer(arg, " id=0x%x", id);
229    len -= HEADERLEN;
230
231    switch (code) {
232    case CBCP_REQ:
233    case CBCP_RESP:
234    case CBCP_ACK:
235        while(len >= 2) {
236	    GETCHAR(opt, p);
237	    GETCHAR(olen, p);
238
239	    if (olen < 2 || olen > len) {
240	        break;
241	    }
242
243	    printer(arg, " <");
244	    len -= olen;
245
246	    if (opt >= 1 && opt <= sizeof(cbcp_optionnames) / sizeof(char *))
247	    	printer(arg, " %s", cbcp_optionnames[opt-1]);
248	    else
249	        printer(arg, " option=0x%x", opt);
250
251	    if (olen > 2) {
252	        GETCHAR(delay, p);
253		printer(arg, " delay = %d", delay);
254	    }
255
256	    if (olen > 3) {
257	        int addrt;
258		char str[256];
259
260		GETCHAR(addrt, p);
261		memcpy(str, p, olen - 4);
262		str[olen - 4] = 0;
263		printer(arg, " number = %s", str);
264	    }
265	    printer(arg, ">");
266	    break;
267	}
268
269    default:
270	break;
271    }
272
273    for (; len > 0; --len) {
274	GETCHAR(code, p);
275	printer(arg, " %.2x", code);
276    }
277
278    return p - pstart;
279}
280
281/* received CBCP request */
282static void
283cbcp_recvreq(us, pckt, pcktlen)
284    cbcp_state *us;
285    char *pckt;
286    int pcktlen;
287{
288    u_char type, opt_len, delay, addr_type;
289    char address[256];
290    int len = pcktlen;
291
292    address[0] = 0;
293
294    while (len) {
295        dbglog("length: %d", len);
296
297	GETCHAR(type, pckt);
298	GETCHAR(opt_len, pckt);
299
300	if (opt_len > 2)
301	    GETCHAR(delay, pckt);
302
303	us->us_allowed |= (1 << type);
304
305	switch(type) {
306	case CB_CONF_NO:
307	    dbglog("no callback allowed");
308	    break;
309
310	case CB_CONF_USER:
311	    dbglog("user callback allowed");
312	    if (opt_len > 4) {
313	        GETCHAR(addr_type, pckt);
314		memcpy(address, pckt, opt_len - 4);
315		address[opt_len - 4] = 0;
316		if (address[0])
317		    dbglog("address: %s", address);
318	    }
319	    break;
320
321	case CB_CONF_ADMIN:
322	    dbglog("user admin defined allowed");
323	    break;
324
325	case CB_CONF_LIST:
326	    break;
327	}
328	len -= opt_len;
329    }
330
331    cbcp_resp(us);
332}
333
334static void
335cbcp_resp(us)
336    cbcp_state *us;
337{
338    u_char cb_type;
339    u_char buf[256];
340    u_char *bufp = buf;
341    int len = 0;
342
343    cb_type = us->us_allowed & us->us_type;
344    dbglog("cbcp_resp cb_type=%d", cb_type);
345
346
347    if (cb_type & ( 1 << CB_CONF_USER ) ) {
348	dbglog("cbcp_resp CONF_USER");
349	PUTCHAR(CB_CONF_USER, bufp);
350	len = 3 + 1 + strlen(us->us_number) + 1;
351	PUTCHAR(len , bufp);
352	PUTCHAR(5, bufp); /* delay */
353	PUTCHAR(1, bufp);
354	BCOPY(us->us_number, bufp, strlen(us->us_number) + 1);
355	cbcp_send(us, CBCP_RESP, buf, len);
356	return;
357    }
358
359    if (cb_type & ( 1 << CB_CONF_ADMIN ) ) {
360	dbglog("cbcp_resp CONF_ADMIN");
361        PUTCHAR(CB_CONF_ADMIN, bufp);
362	len = 3;
363	PUTCHAR(len, bufp);
364	PUTCHAR(5, bufp); /* delay */
365	cbcp_send(us, CBCP_RESP, buf, len);
366	return;
367    }
368
369    if (cb_type & ( 1 << CB_CONF_NO ) ) {
370        dbglog("cbcp_resp CONF_NO");
371	PUTCHAR(CB_CONF_NO, bufp);
372	len = 3;
373	PUTCHAR(len , bufp);
374	PUTCHAR(0, bufp);
375	cbcp_send(us, CBCP_RESP, buf, len);
376	start_networks();
377	return;
378    }
379}
380
381static void
382cbcp_send(us, code, buf, len)
383    cbcp_state *us;
384    u_char code;
385    u_char *buf;
386    int len;
387{
388    u_char *outp;
389    int outlen;
390
391    outp = outpacket_buf;
392
393    outlen = 4 + len;
394
395    MAKEHEADER(outp, PPP_CBCP);
396
397    PUTCHAR(code, outp);
398    PUTCHAR(us->us_id, outp);
399    PUTSHORT(outlen, outp);
400
401    if (len)
402        BCOPY(buf, outp, len);
403
404    output(us->us_unit, outpacket_buf, outlen + PPP_HDRLEN);
405}
406
407static void
408cbcp_recvack(us, pckt, len)
409    cbcp_state *us;
410    char *pckt;
411    int len;
412{
413    u_char type, delay, addr_type;
414    int opt_len;
415    char address[256];
416
417    if (len) {
418        GETCHAR(type, pckt);
419	GETCHAR(opt_len, pckt);
420
421	if (opt_len > 2)
422	    GETCHAR(delay, pckt);
423
424	if (opt_len > 4) {
425	    GETCHAR(addr_type, pckt);
426	    memcpy(address, pckt, opt_len - 4);
427	    address[opt_len - 4] = 0;
428	    if (address[0])
429	        dbglog("peer will call: %s", address);
430	}
431	if (type == CB_CONF_NO)
432	    return;
433    }
434
435    cbcp_up(us);
436}
437
438/* ok peer will do callback */
439static void
440cbcp_up(us)
441    cbcp_state *us;
442{
443    persist = 0;
444    lcp_close(0, "Call me back, please");
445    status = EXIT_CALLBACK;
446}
447