/*
 * npmpset - Set IP address via Network Peripheral Management Protocol
 * Certain printers (e.g. Epson Stylus) like to be configured this way
 * Matt Chapman <matthewc@cse.unsw.edu.au>, May 2000
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define BROADCAST_ADDR		"255.255.255.255"
#define NPMP_SRV_PORT		8450
#define NPMP_CLI_PORT		16309

#define NPMP_OP_SET		0x03

#define NPMP_IP_ADDRESS		0x01
#define NPMP_IFACE_ETHER1	0xf7
#define NPMP_IFACE_ETHER2	0xf8

void npmp_set_ip(int sock, struct sockaddr_in *addr,
		  unsigned int *ether, unsigned int *ip)
{
	char buff[] = { 0x3f,0xb5,'H','B','M',0x03,
			NPMP_IFACE_ETHER1,3,ether[0],ether[1],ether[2],
			NPMP_IFACE_ETHER2,3,ether[3],ether[4],ether[5],
			NPMP_IP_ADDRESS,4,ip[0],ip[1],ip[2],ip[3] };

	if (sendto(sock, buff, sizeof(buff), 0, (struct sockaddr *)addr,
		   sizeof(*addr)) == -1)
	{
		perror("sendto");
	}
}

int main(int argc, char *argv[])
{
	unsigned int ether[6];
	unsigned int ip[4];
	struct sockaddr_in addr;
	int true = 1;
	int sock;


	if (argc < 3)
	{
		fprintf(stderr, "npmpset: Set IP address via Network Peripheral Management Protocol\n");
		fprintf(stderr, "  %s <ethernet address> <ip address>\n",
			argv[0]);
		return EXIT_FAILURE;
	}

	if (sscanf(argv[1], "%02x:%02x:%02x:%02x:%02x:%02x", &ether[0],
		   &ether[1], &ether[2], &ether[3], &ether[4], &ether[5]) != 6)
	{
		fprintf(stderr, "Malformed ethernet address\n");
		return EXIT_FAILURE;
	}

	if (sscanf(argv[2], "%u.%u.%u.%u", &ip[0], &ip[1], &ip[2], &ip[3]) != 4)
	{
		fprintf(stderr, "Malformed IP address\n");
		return EXIT_FAILURE;
	}

	if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
	{
		perror("socket");
		return EXIT_FAILURE;
	}

	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = INADDR_ANY;
	addr.sin_port = htons(NPMP_CLI_PORT);
	if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1)
	{
		perror("bind");
		close(sock);
		return EXIT_FAILURE;
	}

	if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&true,
			sizeof(true)) == -1)
	{
		perror("setsockopt");
		close(sock);
		return EXIT_FAILURE;
	}

	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr(BROADCAST_ADDR);
	addr.sin_port = htons(NPMP_SRV_PORT);

	npmp_set_ip(sock, &addr, ether, ip);
	close(sock);
	return EXIT_SUCCESS;
}
