chiark / gitweb /
Socket address stuff. Sockid is broken still.
[chiark-tcl.git] / dgram / sockaddr.c
1 /*
2  * struct sockaddr
3  *
4  * syntaxes:
5  *    ddd.ddd.ddd.ddd,nnnn       IPv4 address and port
6  *                                 ... host or port may be `*'
7  *    /abs/path/to/socket        AF_UNIX
8  *    ./rel/path/to/socket       AF_UNIX
9  */
10
11 #include "hbytes.h"
12 #include "tables.h"
13
14 #define SOCKADDR_LEN(sa) ((sa)->end - (sa)->begin)
15
16 /* parsing */
17
18 int pat_sockaddr(Tcl_Interp *ip, Tcl_Obj *o, SockAddr_Value *val) {
19   int rc;
20
21   rc= Tcl_ConvertToType(ip,o,&sockaddr_type);
22   if (rc) return rc;
23
24   *val= *OBJ_SOCKADDR(o);
25   return TCL_OK;
26 }
27   
28 Tcl_Obj *ret_sockaddr(Tcl_Interp *ip, SockAddr_Value val) {
29   Tcl_Obj *o;
30
31   o= Tcl_NewObj();
32   Tcl_InvalidateStringRep(o);
33   *OBJ_SOCKADDR(o)= val;
34   o->typePtr= &sockaddr_type;
35   return o;
36 }
37
38 /* native type methods */
39
40 void sockaddr_clear(SockAddr_Value *v) { v->begin= v->end= 0; }
41
42 void sockaddr_create(SockAddr_Value *v, const struct sockaddr *addr, int len) {
43   Byte *begin;
44   
45   v->begin= begin= TALLOC(len);
46   memcpy(begin, addr, len);
47   v->end= begin + len;
48 }  
49
50 int sockaddr_len(const SockAddr_Value *v) {
51   return SOCKADDR_LEN(v);
52 }
53
54 const struct sockaddr *sockaddr_addr(const SockAddr_Value *v) {
55   return (const void*)v->begin;
56 }
57   
58 void sockaddr_free(const SockAddr_Value *v) {
59   TFREE(v->begin);
60 }
61
62 /* Sockaddr Tcl type */
63
64 static void sockaddr_t_dup(Tcl_Obj *src, Tcl_Obj *dup) {
65   objfreeir(dup);
66   sockaddr_create(OBJ_SOCKADDR(dup),
67                   sockaddr_addr(OBJ_SOCKADDR(src)),
68                   sockaddr_len(OBJ_SOCKADDR(src)));
69 }
70
71 static void sockaddr_t_free(Tcl_Obj *o) {
72   sockaddr_free(OBJ_SOCKADDR(o));
73 }
74
75 static void sockaddr_t_ustr(Tcl_Obj *o) {
76   const struct sockaddr *sa;
77   char i46buf[INET6_ADDRSTRLEN];
78   int al, sl, pl;
79   const char *string, *prepend;
80   
81   sa= sockaddr_addr(OBJ_SOCKADDR(o));
82   al= sockaddr_len(OBJ_SOCKADDR(o));
83     
84   switch (sa->sa_family) {
85   case AF_INET:
86   case AF_INET6:
87     assert(sizeof(i46buf) >= INET_ADDRSTRLEN);
88     assert(al >= sizeof(struct sockaddr_in));
89     inet_ntop(sa->sa_family, sa, i46buf, al);
90     string= i46buf;
91     prepend= "";
92     break;
93
94   case AF_UNIX:
95     assert(al >= sizeof(struct sockaddr_un));
96     string= ((const struct sockaddr_un*)sa)->sun_path;
97     prepend= "";
98     if (!string[0]) string="//"; 
99     else if (string[0] != '/' || string[1] == '/') prepend= "./"; 
100
101   default: /* ouch ! */
102     obj_updatestr_array_prefix(o,(const void*)sa,al,"?");
103     return;
104   }
105
106   pl= strlen(prepend);
107   sl= strlen(string);
108   o->bytes= TALLOC(sl+1);
109   memcpy(o->bytes, prepend, pl);
110   memcpy(o->bytes+pl, string, sl+1);
111 }
112
113 static int sockaddr_t_sfa(Tcl_Interp *ip, Tcl_Obj *o) {
114   union {
115     struct sockaddr_un sun;
116     struct sockaddr_in sin;
117   } s;
118   unsigned long port_l;
119   
120   char *str, *ep, *copy;
121   int sl, pl, iprv;
122   const char *comma, *path;
123
124   str= Tcl_GetStringFromObj(o,0);  assert(str);
125   objfreeir(o);
126   memset(&s,0,sizeof(s));
127
128   if (str[0]=='/' || (str[0]=='.' && str[1]=='/')) {
129     
130     sl= sizeof(s.sun);
131     s.sun.sun_family= AF_UNIX;
132
133     if (strcmp(str,"//")) path= "";
134     else if (memcmp(str,"./",2) && str[2]) path= str+2;
135     else { assert(str[0]=='/' && str[1]!='/'); path=str; }
136
137     if (strlen(str) >= sizeof(s.sun.sun_path))
138       return staticerr(ip, "AF_UNIX path too long");
139
140     strcpy(s.sun.sun_path, str);
141
142   } else if ((comma= strchr(str, ','))) {
143
144     sl= sizeof(s.sin);
145     s.sin.sin_family= AF_INET;
146
147     pl= comma - str;
148     copy= TALLOC(pl+1);
149     memcpy(copy, str, pl);
150     copy[pl]= 0;
151
152     if (!strcmp(copy,"*")) {
153       s.sin.sin_addr.s_addr= INADDR_ANY;
154       iprv= 1;
155     } else {
156       iprv= inet_pton(AF_INET, copy, &s.sin.sin_addr);
157     }
158     TFREE(copy);
159
160     if (!iprv) return staticerr(ip, "bad IPv4 address syntax");
161
162     comma++;
163     if (!strcmp(comma,"*")) {
164       s.sin.sin_port= 0;
165     } else {
166       errno=0; port_l=strtoul(comma,&ep,10);
167       if (errno || *ep) return staticerr(ip, "bad IPv4 port");
168       if (port_l > 65535) return staticerr(ip, "IPv4 port out of range");
169       s.sin.sin_port= htons(port_l);
170     }
171
172   } else {
173
174     return staticerr(ip, "bad socket address syntax");
175
176   }
177
178   sockaddr_create(OBJ_SOCKADDR(o), (void*)&s, sl);
179   
180   o->typePtr = &sockaddr_type;
181   return TCL_OK;
182 }
183
184 Tcl_ObjType sockaddr_type = {
185   "sockaddr",
186   sockaddr_t_free, sockaddr_t_dup, sockaddr_t_ustr, sockaddr_t_sfa
187 };