#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#if INET6
#include <netinet6/in6.h>

#include "support.h"
#endif /* INET6 */

int satoport(char *port, struct sockaddr *sa)
{
  switch(sa->sa_family) {
    case AF_INET:
      sprintf(port, "%u,%u,%u,%u,%u,%u",
	      ((u_int8_t *)(&((struct sockaddr_in *)sa)->sin_addr))[0],
	      ((u_int8_t *)(&((struct sockaddr_in *)sa)->sin_addr))[1],
	      ((u_int8_t *)(&((struct sockaddr_in *)sa)->sin_addr))[2],
	      ((u_int8_t *)(&((struct sockaddr_in *)sa)->sin_addr))[3],
	      ((u_int8_t *)(&((struct sockaddr_in *)sa)->sin_port))[0],
	      ((u_int8_t *)(&((struct sockaddr_in *)sa)->sin_port))[1]);
      return 0;
#if INET6
    case AF_INET6:
      if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)sa)->sin6_addr)) {
	sprintf(port, "%u,%u,%u,%u,%u,%u",
		((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_addr))[12],
		((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_addr))[13],
		((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_addr))[14],
		((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_addr))[15],
		((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_port))[0],
		((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_port))[1]);
	return 0;
      }
      return 1;
#endif /* INET6 */
  }
  return 1;
}

int porttosa(struct sockaddr *sa, char *port, struct sockaddr *protosa)
{
  int proto, i;
  char *c, *c2;
  u_int8_t *p;

  c = port;

#define GET(x) c2 = c; while(*c2 && isdigit(*c2)) c2++; if (*c2 != ',') return -1; *(c2++) = 0; x = atoi(c); c = c2;

  memcpy(sa, protosa, NRL_SA_LEN(protosa));

  i = 4;

  switch(sa->sa_family) {
#if INET6
    case AF_INET6:
      if (!IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)sa)->sin6_addr))
	return -2;
      p = (u_int8_t *)&((struct sockaddr_in6 *)sa)->sin6_addr + 12;
      break;
#endif /* INET6 */
    case AF_INET:
      p = (u_int8_t *)&((struct sockaddr_in *)sa)->sin_addr;
      break;
    default:
      return -2;
  }

  while(i--) {
    GET(*(p++));
  }

  if (memcmp(sa, protosa, NRL_SA_LEN(sa)))
    return -3;

  switch(sa->sa_family) {
#if INET6
    case AF_INET6:
      p = (u_int8_t *)&((struct sockaddr_in6 *)sa)->sin6_port;
      break;
#endif /* INET6 */
    case AF_INET:
      p = (u_int8_t *)&((struct sockaddr_in *)sa)->sin_port;
      break;
  }

  GET(*(p++));
  c2 = c; while(*c2 && isdigit(*c2)) c2++; if (*c2) return -1; *p = atoi(c);

  switch(sa->sa_family) {
  case AF_INET:
    if (ntohs(((struct sockaddr_in *)sa)->sin_port) < 1024)
      return -3;
    break;
#if INET6
  case AF_INET6:
    if (ntohs(((struct sockaddr_in6 *)sa)->sin6_port) < 1024)
      return -3;
    break;
#endif /* INET6 */
  }
  return 0;
}

int satolport(char *lport, struct sockaddr *sa)
{
  switch(sa->sa_family) {
    case AF_INET:
      sprintf(lport, "4,4,%u,%u,%u,%u,2,%u,%u",
	      ((u_int8_t *)(&((struct sockaddr_in *)sa)->sin_addr))[0],
	      ((u_int8_t *)(&((struct sockaddr_in *)sa)->sin_addr))[1],
	      ((u_int8_t *)(&((struct sockaddr_in *)sa)->sin_addr))[2],
	      ((u_int8_t *)(&((struct sockaddr_in *)sa)->sin_addr))[3],
	      ((u_int8_t *)(&((struct sockaddr_in *)sa)->sin_port))[0],
	      ((u_int8_t *)(&((struct sockaddr_in *)sa)->sin_port))[1]);
      return 0;
#if INET6
    case AF_INET6:
      if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)sa)->sin6_addr)) {
	struct sockaddr_in sin;
	memset(&sin, 0, sizeof(struct sockaddr_in));
	sin.sin_family = AF_INET;
	sin.sin_port = ((struct sockaddr_in6 *)sa)->sin6_port;
	memcpy(&sin.sin_addr, (u_int8_t *)&((struct sockaddr_in6 *)sa)->sin6_addr + 12, sizeof(struct in_addr));

	return satolport(lport, (struct sockaddr *)&sin);
      }

      sprintf(lport, "6,16,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,2,%u,%u",
	      ((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_addr))[0],
	      ((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_addr))[1],
	      ((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_addr))[2],
	      ((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_addr))[3],
	      ((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_addr))[4],
	      ((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_addr))[5],
	      ((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_addr))[6],
	      ((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_addr))[7],
	      ((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_addr))[8],
	      ((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_addr))[9],
	      ((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_addr))[10],
	      ((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_addr))[11],
	      ((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_addr))[12],
	      ((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_addr))[13],
	      ((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_addr))[14],
	      ((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_addr))[15],
	      ((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_port))[0],
	      ((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_port))[1]);
      return 0;
#endif /* INET6 */
  }
  return 1;
}

int lporttosa(struct sockaddr *sa, char *lport, struct sockaddr *protosa)
{
  int proto, i;
  char *c, *c2;
  u_int8_t *p;

  c = lport;

#define GET(x) c2 = c; while(*c2 && isdigit(*c2)) c2++; if (*c2 != ',') return -1; *(c2++) = 0; x = atoi(c); c = c2;

  GET(proto);
  GET(i);

  memcpy(sa, protosa, NRL_SA_LEN(protosa));

  switch(proto) {
#if INET6
    case 6:
      if (i != sizeof(struct in6_addr))
	return -1;

      switch(sa->sa_family) {
        case AF_INET6:
	  p = (u_int8_t *)&((struct sockaddr_in6 *)sa)->sin6_addr;
	  break;
        default:
	  return -2;
      }
      break;
#endif /* INET6 */
    case 4:
      if (i != sizeof(struct in_addr))
	return -1;

      switch(sa->sa_family) {
#if INET6
        case AF_INET6:
	  if (!IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)sa)->sin6_addr))
	    return -2;
	  p = (u_int8_t *)&((struct sockaddr_in6 *)sa)->sin6_addr + 12;
	  break;
#endif /* INET6 */
        case AF_INET:
	  p = (u_int8_t *)&((struct sockaddr_in *)sa)->sin_addr;
	  break;
        default:
	  return -2;
      }
      break;
    default:
      return -1;
  }

  while(i--) {
    GET(*(p++));
  }

  GET(i);

  switch(proto) {
#if INET6
    case 6:
      if (i != 2)
	return -1;

      switch(sa->sa_family) {
        case AF_INET6:
	  p = (u_int8_t *)&((struct sockaddr_in6 *)sa)->sin6_port;
	  break;
      }
      break;
#endif /* INET6 */
    case 4:
      if (i != 2)
	return -1;

      switch(sa->sa_family) {
#if INET6
        case AF_INET6:
	  p = (u_int8_t *)&((struct sockaddr_in6 *)sa)->sin6_port;
	  break;
#endif /* INET6 */
        case AF_INET:
	  p = (u_int8_t *)&((struct sockaddr_in *)sa)->sin_port;
	  break;
      }
      break;
  }

  if (memcmp(sa, protosa, NRL_SA_LEN(sa)))
    return -3;

  while(i-- > 1)
    GET(*(p++));

  c2 = c; while(*c2 && isdigit(*c2)) c2++; if (*c2) return -1; *p = atoi(c);

  switch(sa->sa_family) {
  case AF_INET:
    if (ntohs(((struct sockaddr_in *)sa)->sin_port) < 1024)
      return -3;
    break;
#if INET6
  case AF_INET6:
    if (ntohs(((struct sockaddr_in6 *)sa)->sin6_port) < 1024)
      return -3;
    break;
#endif /* INET6 */
  }
  return 0;
}

int satosport(char *sport, struct sockaddr *sa)
{
  switch(sa->sa_family) {
    case AF_INET:
      sprintf(sport, "%u,%u",
	      ((u_int8_t *)(&((struct sockaddr_in *)sa)->sin_port))[0],
	      ((u_int8_t *)(&((struct sockaddr_in *)sa)->sin_port))[1]);
      return 0;
#if INET6
    case AF_INET6:
      sprintf(sport, "%u,%u",
	      ((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_port))[0],
	      ((u_int8_t *)(&((struct sockaddr_in6 *)sa)->sin6_port))[1]);
      return 0;
#endif /* INET6 */
  }
  return 1;
}

int sporttosa(struct sockaddr *sa, char *sport, struct sockaddr *protosa)
{
  int i;
  char *c, *c2;
  u_int8_t *p;

  c = sport;

#define GET(x) c2 = c; while(*c2 && isdigit(*c2)) c2++; if (*c2 != ',') return -1; *(c2++) = 0; x = atoi(c); c = c2;

  memcpy(sa, protosa, NRL_SA_LEN(protosa));

  switch(sa->sa_family) {
    case AF_INET:
      p = (u_int8_t *)&((struct sockaddr_in *)sa)->sin_port;
      break;
#if INET6
    case AF_INET6:
      p = (u_int8_t *)&((struct sockaddr_in6 *)sa)->sin6_port;
      break;
#endif /* INET6 */
     default:
       return -1;
  }

  GET(*(p++));
  c2 = c; while(*c2 && isdigit(*c2)) c2++; if (*c2) return -1; *p = atoi(c);

  switch(sa->sa_family) {
  case AF_INET:
    if (ntohs(((struct sockaddr_in *)sa)->sin_port) < 1024)
      return -3;
    break;
#if INET6
  case AF_INET6:
    if (ntohs(((struct sockaddr_in6 *)sa)->sin6_port) < 1024)
      return -3;
    break;
#endif /* INET6 */
  }
  return 0;
}
