152419Sjulian/*
252419Sjulian * msg.c
352419Sjulian *
452419Sjulian * Copyright (c) 1996-1999 Whistle Communications, Inc.
552419Sjulian * All rights reserved.
652419Sjulian *
752419Sjulian * Subject to the following obligations and disclaimer of warranty, use and
852419Sjulian * redistribution of this software, in source or object code forms, with or
952419Sjulian * without modifications are expressly permitted by Whistle Communications;
1052419Sjulian * provided, however, that:
1152419Sjulian * 1. Any and all reproductions of the source or object code must include the
1252419Sjulian *    copyright notice above and the following disclaimer of warranties; and
1352419Sjulian * 2. No rights are granted, in any manner or form, to use Whistle
1452419Sjulian *    Communications, Inc. trademarks, including the mark "WHISTLE
1552419Sjulian *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1652419Sjulian *    such appears in the above copyright notice or in the software.
1752419Sjulian *
1852419Sjulian * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
1952419Sjulian * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
2052419Sjulian * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
2152419Sjulian * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2252419Sjulian * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
2352419Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
2452419Sjulian * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2552419Sjulian * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
2652419Sjulian * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
2752419Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
2852419Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
2952419Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
3052419Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
3152419Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3252419Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3352419Sjulian * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3452419Sjulian * OF SUCH DAMAGE.
3552419Sjulian *
3652419Sjulian * Author: Archie Cobbs <archie@whistle.com>
3752419Sjulian *
3852419Sjulian * $Whistle: msg.c,v 1.9 1999/01/20 00:57:23 archie Exp $
3952419Sjulian */
4052419Sjulian
4184215Sdillon#include <sys/cdefs.h>
4284215Sdillon__FBSDID("$FreeBSD: releng/11.0/lib/libnetgraph/msg.c 248159 2013-03-11 13:05:11Z glebius $");
4384215Sdillon
4452419Sjulian#include <sys/types.h>
45163469Sglebius#include <sys/socket.h>
4652419Sjulian#include <stdarg.h>
4752419Sjulian#include <netgraph/ng_message.h>
4852419Sjulian#include <netgraph/ng_socket.h>
4952419Sjulian
5052419Sjulian#include "netgraph.h"
5152419Sjulian#include "internal.h"
5252419Sjulian
5352419Sjulian/* Next message token value */
5452419Sjulianstatic int	gMsgId;
5552419Sjulian
5652419Sjulian/* For delivering both messages and replies */
5752419Sjulianstatic int	NgDeliverMsg(int cs, const char *path,
5852419Sjulian		  const struct ng_mesg *hdr, const void *args, size_t arglen);
5952419Sjulian
6052419Sjulian/*
6152419Sjulian * Send a message to a node using control socket node "cs".
6252419Sjulian * Returns -1 if error and sets errno appropriately.
6352419Sjulian * If successful, returns the message ID (token) used.
6452419Sjulian */
6552419Sjulianint
6652419SjulianNgSendMsg(int cs, const char *path,
6752419Sjulian	  int cookie, int cmd, const void *args, size_t arglen)
6852419Sjulian{
6952419Sjulian	struct ng_mesg msg;
7052419Sjulian
7152419Sjulian	/* Prepare message header */
7252419Sjulian	memset(&msg, 0, sizeof(msg));
7352419Sjulian	msg.header.version = NG_VERSION;
7452419Sjulian	msg.header.typecookie = cookie;
7561922Sarchie	if (++gMsgId < 0)
7661922Sarchie		gMsgId = 1;
7761922Sarchie	msg.header.token = gMsgId;
7852419Sjulian	msg.header.flags = NGF_ORIG;
7952419Sjulian	msg.header.cmd = cmd;
80145546Smux	snprintf((char *)msg.header.cmdstr, NG_CMDSTRSIZ, "cmd%d", cmd);
8152419Sjulian
8252419Sjulian	/* Deliver message */
8352419Sjulian	if (NgDeliverMsg(cs, path, &msg, args, arglen) < 0)
8452419Sjulian		return (-1);
8585456Sarchie	return (msg.header.token);
8652419Sjulian}
8752419Sjulian
8852419Sjulian/*
8953913Sarchie * Send a message given in ASCII format. We first ask the node to translate
9053913Sarchie * the command into binary, and then we send the binary.
9153913Sarchie */
9253913Sarchieint
9353913SarchieNgSendAsciiMsg(int cs, const char *path, const char *fmt, ...)
9453913Sarchie{
95125113Sru	struct ng_mesg *reply, *binary, *ascii;
9653913Sarchie	char *buf, *cmd, *args;
9753913Sarchie	va_list fmtargs;
98125113Sru	int token;
9953913Sarchie
10053913Sarchie	/* Parse out command and arguments */
10153913Sarchie	va_start(fmtargs, fmt);
10253913Sarchie	vasprintf(&buf, fmt, fmtargs);
10353913Sarchie	va_end(fmtargs);
10453913Sarchie	if (buf == NULL)
10553913Sarchie		return (-1);
10653913Sarchie
10753913Sarchie	/* Parse out command, arguments */
10853913Sarchie	for (cmd = buf; isspace(*cmd); cmd++)
10953913Sarchie		;
11053913Sarchie	for (args = cmd; *args != '\0' && !isspace(*args); args++)
11153913Sarchie		;
11253913Sarchie	if (*args != '\0') {
11353913Sarchie		while (isspace(*args))
11453913Sarchie			*args++ = '\0';
11553913Sarchie	}
11653913Sarchie
11753913Sarchie	/* Get a bigger buffer to hold inner message header plus arg string */
11853913Sarchie	if ((ascii = malloc(sizeof(struct ng_mesg)
11961188Sarchie	    + strlen(args) + 1)) == NULL) {
12053913Sarchie		free(buf);
12153913Sarchie		return (-1);
12253913Sarchie	}
12353913Sarchie	memset(ascii, 0, sizeof(*ascii));
12453913Sarchie
12553913Sarchie	/* Build inner header (only need cmdstr, arglen, and data fields) */
126145546Smux	strncpy((char *)ascii->header.cmdstr, cmd,
127145546Smux	    sizeof(ascii->header.cmdstr) - 1);
12853913Sarchie	strcpy(ascii->data, args);
12953913Sarchie	ascii->header.arglen = strlen(ascii->data) + 1;
13053913Sarchie	free(buf);
13153913Sarchie
13253913Sarchie	/* Send node a request to convert ASCII to binary */
13353913Sarchie	if (NgSendMsg(cs, path, NGM_GENERIC_COOKIE, NGM_ASCII2BINARY,
134125104Sru	    (u_char *)ascii, sizeof(*ascii) + ascii->header.arglen) < 0) {
135125104Sru		free(ascii);
13653913Sarchie		return (-1);
137125104Sru	}
138125104Sru	free(ascii);
13953913Sarchie
14053913Sarchie	/* Get reply */
141125113Sru	if (NgAllocRecvMsg(cs, &reply, NULL) < 0)
14253913Sarchie		return (-1);
14353913Sarchie
14453913Sarchie	/* Now send binary version */
145125113Sru	binary = (struct ng_mesg *)reply->data;
14661922Sarchie	if (++gMsgId < 0)
14761922Sarchie		gMsgId = 1;
14861922Sarchie	binary->header.token = gMsgId;
149141307Sglebius	binary->header.version = NG_VERSION;
15061922Sarchie	if (NgDeliverMsg(cs,
151125113Sru	    path, binary, binary->data, binary->header.arglen) < 0) {
152125113Sru		free(reply);
15361922Sarchie		return (-1);
154125113Sru	}
155125113Sru	token = binary->header.token;
156125113Sru	free(reply);
157125113Sru	return (token);
15853913Sarchie}
15953913Sarchie
16053913Sarchie/*
16152419Sjulian * Send a message that is a reply to a previously received message.
16252419Sjulian * Returns -1 and sets errno on error, otherwise returns zero.
16352419Sjulian */
16452419Sjulianint
16552419SjulianNgSendReplyMsg(int cs, const char *path,
16652419Sjulian	const struct ng_mesg *msg, const void *args, size_t arglen)
16752419Sjulian{
16852419Sjulian	struct ng_mesg rep;
16952419Sjulian
17052419Sjulian	/* Prepare message header */
17152419Sjulian	rep = *msg;
17252419Sjulian	rep.header.flags = NGF_RESP;
17352419Sjulian
17452419Sjulian	/* Deliver message */
17552419Sjulian	return (NgDeliverMsg(cs, path, &rep, args, arglen));
17652419Sjulian}
17752419Sjulian
17852419Sjulian/*
17952419Sjulian * Send a message to a node using control socket node "cs".
18052419Sjulian * Returns -1 if error and sets errno appropriately, otherwise zero.
18152419Sjulian */
18252419Sjulianstatic int
18352419SjulianNgDeliverMsg(int cs, const char *path,
18452419Sjulian	const struct ng_mesg *hdr, const void *args, size_t arglen)
18552419Sjulian{
186122649Sharti	u_char sgbuf[NG_PATHSIZ + NGSA_OVERHEAD];
18752419Sjulian	struct sockaddr_ng *const sg = (struct sockaddr_ng *) sgbuf;
18852419Sjulian	u_char *buf = NULL;
18952419Sjulian	struct ng_mesg *msg;
19052419Sjulian	int errnosv = 0;
19152419Sjulian	int rtn = 0;
19252419Sjulian
19352419Sjulian	/* Sanity check */
19452419Sjulian	if (args == NULL)
19552419Sjulian		arglen = 0;
19652419Sjulian
19752419Sjulian	/* Get buffer */
19852419Sjulian	if ((buf = malloc(sizeof(*msg) + arglen)) == NULL) {
19952419Sjulian		errnosv = errno;
20052419Sjulian		if (_gNgDebugLevel >= 1)
20152419Sjulian			NGLOG("malloc");
20252419Sjulian		rtn = -1;
20352419Sjulian		goto done;
20452419Sjulian	}
20552419Sjulian	msg = (struct ng_mesg *) buf;
20652419Sjulian
20752419Sjulian	/* Finalize message */
20852419Sjulian	*msg = *hdr;
20952419Sjulian	msg->header.arglen = arglen;
21052419Sjulian	memcpy(msg->data, args, arglen);
21152419Sjulian
21252419Sjulian	/* Prepare socket address */
21352419Sjulian	sg->sg_family = AF_NETGRAPH;
214122649Sharti	/* XXX handle overflow */
215122649Sharti	strlcpy(sg->sg_data, path, NG_PATHSIZ);
216122649Sharti	sg->sg_len = strlen(sg->sg_data) + 1 + NGSA_OVERHEAD;
21752419Sjulian
21852419Sjulian	/* Debugging */
21952419Sjulian	if (_gNgDebugLevel >= 2) {
22052419Sjulian		NGLOGX("SENDING %s:",
22152419Sjulian		    (msg->header.flags & NGF_RESP) ? "RESPONSE" : "MESSAGE");
22252419Sjulian		_NgDebugSockaddr(sg);
22353913Sarchie		_NgDebugMsg(msg, sg->sg_data);
22452419Sjulian	}
22552419Sjulian
22652419Sjulian	/* Send it */
22752419Sjulian	if (sendto(cs, msg, sizeof(*msg) + arglen,
22852419Sjulian		   0, (struct sockaddr *) sg, sg->sg_len) < 0) {
22952419Sjulian		errnosv = errno;
23052419Sjulian		if (_gNgDebugLevel >= 1)
23152419Sjulian			NGLOG("sendto(%s)", sg->sg_data);
23252419Sjulian		rtn = -1;
23352419Sjulian		goto done;
23452419Sjulian	}
23552419Sjulian
236154265Sglebius	/* Wait for reply if there should be one. */
237248159Sglebius	if (msg->header.cmd & NGM_HASREPLY && !(msg->header.flags & NGF_RESP)) {
238169551Smav		struct pollfd rfds;
239154265Sglebius		int n;
240154265Sglebius
241169551Smav		rfds.fd = cs;
242169551Smav		rfds.events = POLLIN;
243169551Smav		rfds.revents = 0;
244169551Smav		n = poll(&rfds, 1, INFTIM);
245154265Sglebius		if (n == -1) {
246154265Sglebius			errnosv = errno;
247154265Sglebius			if (_gNgDebugLevel >= 1)
248169551Smav				NGLOG("poll");
249154265Sglebius			rtn = -1;
250154265Sglebius		}
251154265Sglebius	}
252154265Sglebius
25352419Sjuliandone:
25452419Sjulian	/* Done */
25552419Sjulian	free(buf);		/* OK if buf is NULL */
25652419Sjulian	errno = errnosv;
25752419Sjulian	return (rtn);
25852419Sjulian}
25952419Sjulian
26052419Sjulian/*
26152419Sjulian * Receive a control message.
26252419Sjulian *
26352419Sjulian * On error, this returns -1 and sets errno.
26452419Sjulian * Otherwise, it returns the length of the received reply.
26552419Sjulian */
26652419Sjulianint
26752419SjulianNgRecvMsg(int cs, struct ng_mesg *rep, size_t replen, char *path)
26852419Sjulian{
269122649Sharti	u_char sgbuf[NG_PATHSIZ + NGSA_OVERHEAD];
27052419Sjulian	struct sockaddr_ng *const sg = (struct sockaddr_ng *) sgbuf;
271145546Smux	socklen_t sglen = sizeof(sgbuf);
272145546Smux	int len, errnosv;
27352419Sjulian
27452419Sjulian	/* Read reply */
27552419Sjulian	len = recvfrom(cs, rep, replen, 0, (struct sockaddr *) sg, &sglen);
27652419Sjulian	if (len < 0) {
27752419Sjulian		errnosv = errno;
27852419Sjulian		if (_gNgDebugLevel >= 1)
27952419Sjulian			NGLOG("recvfrom");
28052419Sjulian		goto errout;
28152419Sjulian	}
28252419Sjulian	if (path != NULL)
283122649Sharti		strlcpy(path, sg->sg_data, NG_PATHSIZ);
28452419Sjulian
28552419Sjulian	/* Debugging */
28652419Sjulian	if (_gNgDebugLevel >= 2) {
28752419Sjulian		NGLOGX("RECEIVED %s:",
28852419Sjulian		    (rep->header.flags & NGF_RESP) ? "RESPONSE" : "MESSAGE");
28952419Sjulian		_NgDebugSockaddr(sg);
29053913Sarchie		_NgDebugMsg(rep, sg->sg_data);
29152419Sjulian	}
29252419Sjulian
29352419Sjulian	/* Done */
29452419Sjulian	return (len);
29552419Sjulian
29652419Sjulianerrout:
29752419Sjulian	errno = errnosv;
29852419Sjulian	return (-1);
29952419Sjulian}
30052419Sjulian
30153913Sarchie/*
302125113Sru * Identical to NgRecvMsg() except buffer is dynamically allocated.
303125113Sru */
304125113Sruint
305125113SruNgAllocRecvMsg(int cs, struct ng_mesg **rep, char *path)
306125113Sru{
307125113Sru	int len;
308125113Sru	socklen_t optlen;
309125113Sru
310125113Sru	optlen = sizeof(len);
311125113Sru	if (getsockopt(cs, SOL_SOCKET, SO_RCVBUF, &len, &optlen) == -1 ||
312125113Sru	    (*rep = malloc(len)) == NULL)
313125113Sru		return (-1);
314125113Sru	if ((len = NgRecvMsg(cs, *rep, len, path)) < 0)
315125113Sru		free(*rep);
316125113Sru	return (len);
317125113Sru}
318125113Sru
319125113Sru/*
32053913Sarchie * Receive a control message and convert the arguments to ASCII
32153913Sarchie */
32253913Sarchieint
32353913SarchieNgRecvAsciiMsg(int cs, struct ng_mesg *reply, size_t replen, char *path)
32453913Sarchie{
32553913Sarchie	struct ng_mesg *msg, *ascii;
32653913Sarchie	int bufSize, errnosv;
32753913Sarchie	u_char *buf;
32853913Sarchie
32953913Sarchie	/* Allocate buffer */
33053913Sarchie	bufSize = 2 * sizeof(*reply) + replen;
33153913Sarchie	if ((buf = malloc(bufSize)) == NULL)
33253913Sarchie		return (-1);
33353913Sarchie	msg = (struct ng_mesg *)buf;
33453913Sarchie	ascii = (struct ng_mesg *)msg->data;
33553913Sarchie
33653913Sarchie	/* Get binary message */
33753913Sarchie	if (NgRecvMsg(cs, msg, bufSize, path) < 0)
33853913Sarchie		goto fail;
33953913Sarchie	memcpy(reply, msg, sizeof(*msg));
34053913Sarchie
34153913Sarchie	/* Ask originating node to convert the arguments to ASCII */
34253913Sarchie	if (NgSendMsg(cs, path, NGM_GENERIC_COOKIE,
34353913Sarchie	    NGM_BINARY2ASCII, msg, sizeof(*msg) + msg->header.arglen) < 0)
34453913Sarchie		goto fail;
34553913Sarchie	if (NgRecvMsg(cs, msg, bufSize, NULL) < 0)
34653913Sarchie		goto fail;
34753913Sarchie
34853913Sarchie	/* Copy result to client buffer */
34953913Sarchie	if (sizeof(*ascii) + ascii->header.arglen > replen) {
35053913Sarchie		errno = ERANGE;
35153913Sarchiefail:
35253913Sarchie		errnosv = errno;
35353913Sarchie		free(buf);
35453913Sarchie		errno = errnosv;
35553913Sarchie		return (-1);
35653913Sarchie	}
35753913Sarchie	strncpy(reply->data, ascii->data, ascii->header.arglen);
35853913Sarchie
35953913Sarchie	/* Done */
36053913Sarchie	free(buf);
36153913Sarchie	return (0);
36253913Sarchie}
36353913Sarchie
364125113Sru/*
365125113Sru * Identical to NgRecvAsciiMsg() except buffer is dynamically allocated.
366125113Sru */
367125113Sruint
368125113SruNgAllocRecvAsciiMsg(int cs, struct ng_mesg **reply, char *path)
369125113Sru{
370125113Sru	int len;
371125113Sru	socklen_t optlen;
372125113Sru
373125113Sru	optlen = sizeof(len);
374125113Sru	if (getsockopt(cs, SOL_SOCKET, SO_RCVBUF, &len, &optlen) == -1 ||
375125113Sru	    (*reply = malloc(len)) == NULL)
376125113Sru		return (-1);
377125113Sru	if ((len = NgRecvAsciiMsg(cs, *reply, len, path)) < 0)
378125113Sru		free(*reply);
379125113Sru	return (len);
380125113Sru}
381