chiark / gitweb /
Socket address stuff. Sockid is broken still.
[chiark-tcl.git] / dgram / sockaddr.c
diff --git a/dgram/sockaddr.c b/dgram/sockaddr.c
new file mode 100644 (file)
index 0000000..bedc109
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * struct sockaddr
+ *
+ * syntaxes:
+ *    ddd.ddd.ddd.ddd,nnnn       IPv4 address and port
+ *                                 ... host or port may be `*'
+ *    /abs/path/to/socket        AF_UNIX
+ *    ./rel/path/to/socket       AF_UNIX
+ */
+
+#include "hbytes.h"
+#include "tables.h"
+
+#define SOCKADDR_LEN(sa) ((sa)->end - (sa)->begin)
+
+/* parsing */
+
+int pat_sockaddr(Tcl_Interp *ip, Tcl_Obj *o, SockAddr_Value *val) {
+  int rc;
+
+  rc= Tcl_ConvertToType(ip,o,&sockaddr_type);
+  if (rc) return rc;
+
+  *val= *OBJ_SOCKADDR(o);
+  return TCL_OK;
+}
+  
+Tcl_Obj *ret_sockaddr(Tcl_Interp *ip, SockAddr_Value val) {
+  Tcl_Obj *o;
+
+  o= Tcl_NewObj();
+  Tcl_InvalidateStringRep(o);
+  *OBJ_SOCKADDR(o)= val;
+  o->typePtr= &sockaddr_type;
+  return o;
+}
+
+/* native type methods */
+
+void sockaddr_clear(SockAddr_Value *v) { v->begin= v->end= 0; }
+
+void sockaddr_create(SockAddr_Value *v, const struct sockaddr *addr, int len) {
+  Byte *begin;
+  
+  v->begin= begin= TALLOC(len);
+  memcpy(begin, addr, len);
+  v->end= begin + len;
+}  
+
+int sockaddr_len(const SockAddr_Value *v) {
+  return SOCKADDR_LEN(v);
+}
+
+const struct sockaddr *sockaddr_addr(const SockAddr_Value *v) {
+  return (const void*)v->begin;
+}
+  
+void sockaddr_free(const SockAddr_Value *v) {
+  TFREE(v->begin);
+}
+
+/* Sockaddr Tcl type */
+
+static void sockaddr_t_dup(Tcl_Obj *src, Tcl_Obj *dup) {
+  objfreeir(dup);
+  sockaddr_create(OBJ_SOCKADDR(dup),
+                 sockaddr_addr(OBJ_SOCKADDR(src)),
+                 sockaddr_len(OBJ_SOCKADDR(src)));
+}
+
+static void sockaddr_t_free(Tcl_Obj *o) {
+  sockaddr_free(OBJ_SOCKADDR(o));
+}
+
+static void sockaddr_t_ustr(Tcl_Obj *o) {
+  const struct sockaddr *sa;
+  char i46buf[INET6_ADDRSTRLEN];
+  int al, sl, pl;
+  const char *string, *prepend;
+  
+  sa= sockaddr_addr(OBJ_SOCKADDR(o));
+  al= sockaddr_len(OBJ_SOCKADDR(o));
+    
+  switch (sa->sa_family) {
+  case AF_INET:
+  case AF_INET6:
+    assert(sizeof(i46buf) >= INET_ADDRSTRLEN);
+    assert(al >= sizeof(struct sockaddr_in));
+    inet_ntop(sa->sa_family, sa, i46buf, al);
+    string= i46buf;
+    prepend= "";
+    break;
+
+  case AF_UNIX:
+    assert(al >= sizeof(struct sockaddr_un));
+    string= ((const struct sockaddr_un*)sa)->sun_path;
+    prepend= "";
+    if (!string[0]) string="//"; 
+    else if (string[0] != '/' || string[1] == '/') prepend= "./"; 
+
+  default: /* ouch ! */
+    obj_updatestr_array_prefix(o,(const void*)sa,al,"?");
+    return;
+  }
+
+  pl= strlen(prepend);
+  sl= strlen(string);
+  o->bytes= TALLOC(sl+1);
+  memcpy(o->bytes, prepend, pl);
+  memcpy(o->bytes+pl, string, sl+1);
+}
+
+static int sockaddr_t_sfa(Tcl_Interp *ip, Tcl_Obj *o) {
+  union {
+    struct sockaddr_un sun;
+    struct sockaddr_in sin;
+  } s;
+  unsigned long port_l;
+  
+  char *str, *ep, *copy;
+  int sl, pl, iprv;
+  const char *comma, *path;
+
+  str= Tcl_GetStringFromObj(o,0);  assert(str);
+  objfreeir(o);
+  memset(&s,0,sizeof(s));
+
+  if (str[0]=='/' || (str[0]=='.' && str[1]=='/')) {
+    
+    sl= sizeof(s.sun);
+    s.sun.sun_family= AF_UNIX;
+
+    if (strcmp(str,"//")) path= "";
+    else if (memcmp(str,"./",2) && str[2]) path= str+2;
+    else { assert(str[0]=='/' && str[1]!='/'); path=str; }
+
+    if (strlen(str) >= sizeof(s.sun.sun_path))
+      return staticerr(ip, "AF_UNIX path too long");
+
+    strcpy(s.sun.sun_path, str);
+
+  } else if ((comma= strchr(str, ','))) {
+
+    sl= sizeof(s.sin);
+    s.sin.sin_family= AF_INET;
+
+    pl= comma - str;
+    copy= TALLOC(pl+1);
+    memcpy(copy, str, pl);
+    copy[pl]= 0;
+
+    if (!strcmp(copy,"*")) {
+      s.sin.sin_addr.s_addr= INADDR_ANY;
+      iprv= 1;
+    } else {
+      iprv= inet_pton(AF_INET, copy, &s.sin.sin_addr);
+    }
+    TFREE(copy);
+
+    if (!iprv) return staticerr(ip, "bad IPv4 address syntax");
+
+    comma++;
+    if (!strcmp(comma,"*")) {
+      s.sin.sin_port= 0;
+    } else {
+      errno=0; port_l=strtoul(comma,&ep,10);
+      if (errno || *ep) return staticerr(ip, "bad IPv4 port");
+      if (port_l > 65535) return staticerr(ip, "IPv4 port out of range");
+      s.sin.sin_port= htons(port_l);
+    }
+
+  } else {
+
+    return staticerr(ip, "bad socket address syntax");
+
+  }
+
+  sockaddr_create(OBJ_SOCKADDR(o), (void*)&s, sl);
+  
+  o->typePtr = &sockaddr_type;
+  return TCL_OK;
+}
+
+Tcl_ObjType sockaddr_type = {
+  "sockaddr",
+  sockaddr_t_free, sockaddr_t_dup, sockaddr_t_ustr, sockaddr_t_sfa
+};