/*
 * Written by Stig Venaas <venaas@uninett.no> January 2003
 *
 *
 * To use it, you must alter two lines at beginning of main().
 *
 * That is, the line
 * const char *source = "2001:700:1:0:290:27ff:fe50:6bfa";
 * should be changed to address of host running this tool. It's the
 * source that will be specified in the IPv6 announcements.
 *
 * Also, the line
 * const char *prefix = "6 ff3e:30:2001:700:1:ffff:";
 * must be changed to the /96 prefix you use for the gateway.
 *
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/ip6.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

int
udpread(int s, void *buf, int count)
{
    struct sockaddr_in6 from;
    size_t len = sizeof(from);

    return recvfrom(s, buf, count, 0, (struct sockaddr *)&from, &len);
}

char *findtype(char type, char *buf)
{
    while (*buf) {
	if (buf[0] == '\n' && buf[1] == type && buf[2] == '=')
	    return buf + 3;
	buf++;
    }
    return NULL;
}

void
writevalue(char *s)
{
    char *p, c;

    for (p = s; *p; p++) {
	if (*p == '\n')
	    break;
    }
    c = *p;
    *p = '\0';
    printf(": %s\n", s);
    *p = c;
}

int
main()
{
    const size_t buflen = 2048;
    const char *source = "2001:700:1:0:290:27ff:fe50:6bfa";
    const char *prefix = "6 ff3e:30:2001:700:1:ffff:";
    char in[buflen], out[buflen], *name, *sdp, *p, *q, *l, *t;
    int s4, s6;
    int cnt, trans;
    int hops = 30;
    struct ip_mreq mreq;
    struct sockaddr_in sa4;
    struct sockaddr_in6 sa6;

    s4 = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (s4 < 0) {
	fprintf(stderr, "IPv4 socket failed\n");
	exit(1);
    }
    s6 = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
    if (s6 < 0) {
	fprintf(stderr, "IPv6 socket failed\n");
	exit(1);
    }

    setsockopt(s6, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof(hops));

    memset(&mreq, 0, sizeof(mreq));
    inet_pton(AF_INET, "224.2.127.254", &mreq.imr_multiaddr);
    setsockopt(s4, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));

    memset(&sa4, 0, sizeof(sa4));
    sa4.sin_addr.s_addr = mreq.imr_multiaddr.s_addr;
    sa4.sin_port = htons(9875);
    bind(s4, (struct sockaddr *)&sa4, sizeof(sa4));

    memset(&sa6, 0, sizeof(sa6));
    inet_pton(AF_INET6, "ff0e::2:7ffe", &sa6.sin6_addr);
    sa6.sin6_port = htons(9875);

    while ((cnt = udpread(s4, in, buflen))) {
	/* A, E, C flags and authlen must be 0 and 1500 > length > 32 */
	if (cnt < 33 || cnt > 1499 || in[0] & 19 || in[1]) {
	    printf("Ignoring\n");
	    continue;
	}
	in[cnt] = '\0';
	out[0] = in[0] | 16;
	out[1] = '\0';
	out[2] = in[2];
	out[3] = in[3];
	inet_pton(AF_INET6, source, out + 4);
	q = out + 20;
	p = in + 8;
	l = p;
	if (!strcmp(p, "application/sdp"))
	    p += strlen("application/sdp") + 1;
	if (memcmp(p, "v=0", 3)) {
	    printf("SDP not starting with v=0\n");
	    continue;
	}

	sdp = p;
	if (!(p = findtype('o', p))) {
	    printf("no o= line\n");
	    continue;
	}
	memcpy(q, l, p - l);
	q += p - l;
	strncpy(q, "mcgw_", 5);
	q += 5;
	l = p;
	if (!(p = findtype('s', p))) {
	    printf("no s= line\n");
	    continue;
	}
	name = p;
	memcpy(q, l, p - l);
	q += p - l;
	strncpy(q, "zmcgw: ", 7);
	q += 7;

	/* Finally we need to translate IPv4 addresses in c= lines */
	trans = 0;
	l = p;
	while ((p = findtype('c', p))) {
	    if (q - out + (p - l) > 1500)
		break;
	    if (strncmp(p, "IN IP4 ", 7))
		continue;

	    /* rewrite address */
	    p += 7;
	    for (t = p; isprint(*t); t++)
		if (*t == '/')
		    break;
	    if (*t != '/') {
		printf("No ttl?");
		writevalue(name);
		continue;
	    }
	    *t = '\0';
	    /* check if multicast address */
	    if (inet_pton(AF_INET, p, &sa4.sin_addr) <= 0) {
		printf("Not an IPv4 address, ignoring");
		writevalue(name);
		continue;
	    }
	    if (!IN_MULTICAST(ntohl(sa4.sin_addr.s_addr))) {
		printf("Not an IPv4 multicast address, ignoring");
		writevalue(name);
		continue;
	    }
	    *t = '/';
	    memcpy(q, l, p - l - 2);
	    q += p - l - 2;
	    strcpy(q, prefix);
	    q += strlen(prefix);
	    l = p;
	    trans = 1;
	}
	if (p) {
	    printf("out packet too large, skipping");
	    writevalue(name);
	    continue;
	}
	if (!trans) {
	    printf("Nothing translated, skipping");
	    writevalue(name);
	    continue;
	}

	strcpy(q, l);
	q += strlen(l);
	printf("Sending");
	writevalue(name);
	*q = '\0';
#if 0
	printf("%s\n", out); 
	printf("outlen=%d\n", q - out);
#endif
	sendto(s6, out, q - out, 0, (struct sockaddr *)&sa6, sizeof(sa6));
    }
    return 1;
}
