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