1/*
2 * Copyright (c) 2000, 2001 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * Modification History
26 *
27 * Oct 25, 2002                 Christophe Allie <callie@apple.com>
28 * - use ServiceID instead of LinkID
29 *
30 * Feb 28, 2002                 Christophe Allie <callie@apple.com>
31 * - socket API fixes
32 *
33 * Feb 10, 2001                 Allan Nathanson <ajn@apple.com>
34 * - cleanup API
35 *
36 * Feb 2000                     Christophe Allie <callie@apple.com>
37 * - initial revision
38 */
39
40#include <stdio.h>
41#include <unistd.h>
42#include <sys/types.h>
43#include <sys/errno.h>
44#include <sys/socket.h>
45#include <sys/un.h>
46#include <CoreFoundation/CoreFoundation.h>
47
48#include "ppp_msg.h"
49#include "ppplib.h"
50
51__private_extern__
52int
53PPPInit(int *ref)
54{
55	int			sock;
56	int			status;
57	struct sockaddr_un	sun;
58
59	sock = socket(AF_LOCAL, SOCK_STREAM, 0);
60
61	bzero(&sun, sizeof(sun));
62	sun.sun_family = AF_LOCAL;
63	strncpy(sun.sun_path, PPP_PATH, sizeof(sun.sun_path));
64
65	status = connect(sock,  (struct sockaddr *)&sun, sizeof(sun));
66	if (status < 0) {
67		return errno;
68	}
69
70	*ref = sock;
71	return 0;
72}
73
74
75__private_extern__
76int
77PPPDispose(int ref)
78{
79	if (close(ref) < 0) {
80		return errno;
81	}
82	return 0;
83}
84
85static int
86readn(int ref, void *data, int len)
87{
88    int 	n, left = len;
89    void 	*p = data;
90
91    while (left > 0) {
92        if ((n = read(ref, p, left)) < 0) {
93            if (errno != EINTR)
94                return -1;
95            n = 0;
96        }
97        else if (n == 0)
98            break; /* EOF */
99
100        left -= n;
101        p += n;
102    }
103    return (len - left);
104}
105
106static int
107writen(int ref, void *data, int len)
108{
109    int 	n, left = len;
110    void 	*p = data;
111
112    while (left > 0) {
113        if ((n = write(ref, p, left)) <= 0) {
114            if (errno != EINTR)
115                return -1;
116            n = 0;
117        }
118        left -= n;
119        p += n;
120    }
121    return len;
122}
123
124static int
125PPPExec(int		ref,
126	u_int8_t	*serviceid,
127	u_int32_t	cmd,
128	void		*request,
129	u_int32_t	requestLen,
130	void		**reply,
131	u_int32_t	*replyLen)
132{
133	struct ppp_msg_hdr	msg;
134	char			*buf		= NULL;
135	ssize_t			n;
136
137	bzero(&msg, sizeof(msg));
138	if (serviceid) {
139            // serviceid is present, use it
140            msg.m_flags = USE_SERVICEID;
141            msg.m_link = strlen(serviceid);
142        }
143        else {
144            // no service ID, use the default link
145            msg.m_link = -1;
146        }
147	msg.m_type = cmd;
148	msg.m_len  = ((request != NULL) && (requestLen > 0)) ? requestLen : 0;
149
150	//  send the command
151	if (writen(ref, &msg, sizeof(msg)) < 0) {
152                fprintf(stderr, "PPPExec write() failed: %s\n", strerror(errno));
153                return errno;
154        }
155
156        if (serviceid) {
157            if (writen(ref, serviceid, msg.m_link) < 0) {
158                fprintf(stderr, "PPPExec write() failed: %s\n", strerror(errno));
159                return errno;
160            }
161        }
162
163	if ((request != NULL) && (requestLen > 0)) {
164		if (writen(ref, request, requestLen) < 0) {
165			fprintf(stderr, "PPPExec write() failed: %s\n", strerror(errno));
166			return errno;
167		}
168	}
169
170	// always expect a reply
171	n = readn(ref, &msg, sizeof(msg));
172	if (n == -1) {
173		fprintf(stderr, "PPPExec read() failed: error=%s\n", strerror(errno));
174		return errno;
175	} else if (n != sizeof(msg)) {
176		fprintf(stderr, "PPPExec read() failed: insufficent data, read=%d\n", n);
177		return -1;
178	}
179
180	if (serviceid && msg.m_link) {
181		buf = CFAllocatorAllocate(NULL, msg.m_link, 0);
182		if (buf) {
183			// read reply
184			n = readn(ref, buf, msg.m_link);
185			if (n == -1) {
186				fprintf(stderr, "PPPExec read() failed: error=%s\n", strerror(errno));
187				CFAllocatorDeallocate(NULL, buf);
188				return errno;
189			} else if (n != msg.m_link) {
190				fprintf(stderr, "PPPExec read() failed: insufficent data, read=%d\n", n);
191				CFAllocatorDeallocate(NULL, buf);
192				return -1;
193			}
194			// buf contains the service id we passed in the request
195			CFAllocatorDeallocate(NULL, buf);
196			buf = NULL;
197		}
198	}
199
200	if (msg.m_len) {
201		buf = CFAllocatorAllocate(NULL, msg.m_len, 0);
202		if (buf) {
203			// read reply
204			n = readn(ref, buf, msg.m_len);
205			if (n == -1) {
206				fprintf(stderr, "PPPExec read() failed: error=%s\n", strerror(errno));
207				CFAllocatorDeallocate(NULL, buf);
208				return errno;
209			} else if (n != msg.m_len) {
210				fprintf(stderr, "PPPExec read() failed: insufficent data, read=%d\n", n);
211				CFAllocatorDeallocate(NULL, buf);
212				return -1;
213			}
214		}
215	}
216
217	if (reply && replyLen) {
218		*reply    = buf;
219		*replyLen = msg.m_len;
220	} else if (buf) {
221		// if additional returned data is unwanted
222		CFAllocatorDeallocate(NULL, buf);
223	}
224
225	return msg.m_result;
226}
227
228
229int
230PPPConnect(int ref, u_int8_t *serviceid)
231{
232	int	status;
233
234	status = PPPExec(ref,
235			 serviceid,
236			 PPP_CONNECT,
237			 NULL,
238			 0,
239			 NULL,
240			 NULL);
241	if (status != 0) {
242		fprintf(stderr, "PPPExec(PPP_CONNECT) failed: status = %d\n", status);
243		return status;
244	}
245
246	return status;
247}
248
249
250int
251PPPDisconnect(int ref, u_int8_t *serviceid)
252{
253	int	status;
254
255	status = PPPExec(ref,
256			 serviceid,
257			 PPP_DISCONNECT,
258			 NULL,
259			 0,
260			 NULL,
261			 NULL);
262	if (status != 0) {
263		fprintf(stderr, "PPPExec(PPP_DISCONNECT) failed: status = %d\n", status);
264		return status;
265	}
266
267	return status;
268}
269
270
271__private_extern__
272int
273PPPGetOption(int ref, u_int8_t *serviceid, u_int32_t option, void **data, u_int32_t *dataLen)
274{
275	struct ppp_opt_hdr 	opt;
276	void			*replyBuf	= NULL;
277	u_int32_t		replyBufLen	= 0;
278	int			status;
279
280	bzero(&opt, sizeof(opt));
281	opt.o_type = option;
282
283	status = PPPExec(ref,
284			    serviceid,
285			    PPP_GETOPTION,
286			    (void *)&opt,
287			    sizeof(opt),
288			    &replyBuf,
289			    &replyBufLen);
290	if (status != 0) {
291		fprintf(stderr, "PPPExec(PPP_GETOPTION) failed: status = %d\n", status);
292		*data = NULL;
293		*dataLen = 0;
294		return status;
295	}
296
297	if (replyBuf && (replyBufLen > sizeof(struct ppp_opt_hdr))) {
298		*dataLen = replyBufLen - sizeof(struct ppp_opt_hdr);
299		*data    = CFAllocatorAllocate(NULL, *dataLen, 0);
300		bcopy(((struct ppp_opt *)replyBuf)->o_data, *data, *dataLen);
301	}
302	if (replyBuf)	CFAllocatorDeallocate(NULL, replyBuf);
303
304	return status;
305}
306
307
308__private_extern__
309int
310PPPSetOption(int ref, u_int8_t *serviceid, u_int32_t option, void *data, u_int32_t dataLen)
311{
312	void			*buf;
313	u_long			bufLen;
314	int			status;
315
316	bufLen = sizeof(struct ppp_opt_hdr) + dataLen;
317	buf    = CFAllocatorAllocate(NULL, bufLen, 0);
318
319	bzero((struct ppp_opt_hdr *)buf, sizeof(struct ppp_opt_hdr));
320	((struct ppp_opt_hdr *)buf)->o_type = option;
321	bcopy(data, ((struct ppp_opt *)buf)->o_data, dataLen);
322
323	status = PPPExec(ref,
324			 serviceid,
325			 PPP_SETOPTION,
326			 buf,
327			 bufLen,
328			 NULL,
329			 NULL);
330	if (status != 0) {
331		fprintf(stderr, "PPPExec(PPP_SETOPTION) failed: status = %d\n", status);
332	}
333
334	CFAllocatorDeallocate(NULL, buf);
335
336	return status;
337}
338
339
340__private_extern__
341int
342PPPStatus(int ref, u_int8_t *serviceid, struct ppp_status **stat)
343{
344	void		*replyBuf	= NULL;
345	u_int32_t	replyBufLen	= 0;
346	int		status;
347
348	status = PPPExec(ref,
349			    serviceid,
350			    PPP_STATUS,
351			    NULL,
352			    0,
353			    &replyBuf,
354			    &replyBufLen);
355	if (status != 0) {
356		fprintf(stderr, "PPPExec(PPP_STATUS) failed: status = %d\n", status);
357		return status;
358	}
359
360	if (replyBuf && (replyBufLen == sizeof(struct ppp_status))) {
361		*stat = (struct ppp_status *)replyBuf;
362	} else {
363		if (replyBuf)	CFAllocatorDeallocate(NULL, replyBuf);
364		*stat = NULL;
365		status = -1;
366	}
367
368	return status;
369}
370
371
372__private_extern__
373int
374PPPEnableEvents(int ref, u_int8_t *serviceid, u_char enable)
375{
376	int	status;
377
378	status = PPPExec(ref,
379			 serviceid,
380			 enable ? PPP_ENABLE_EVENT : PPP_DISABLE_EVENT,
381			 NULL,
382			 0,
383			 NULL,
384			 NULL);
385	if (status != 0) {
386		fprintf(stderr,
387		        "PPPExec(%s) failed: status = %d\n",
388			enable ? "PPP_ENABLE_EVENT" : "PPP_DISABLE_EVENT",
389			status);
390		return status;
391	}
392
393	return status;
394}
395