/* "socket.c" internet stream socket support for client/server in SCM * Copyright (C) 1994, 1995, 2006 Free Software Foundation, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see * . */ /* Author: Aubrey Jaffer. * Thanks to Hallvard.Tretteberg@si.sintef.no * who credits NCSA httpd software by Rob McCool 3/21/93 */ #include "scm.h" #ifdef macintosh # define SOCKETDEFS # include "macsocket.h" #endif #include #include #include #include #include #include /* added by Denys Duchier: for bzero */ #ifdef sun # include #endif #ifndef STDC_HEADERS int close P((int fd)); #else /* added by Denys Duchier */ # ifdef SVR4 # include # endif # ifdef __OpenBSD__ # include # endif # ifdef __NetBSD__ # include # endif #endif /* STDC_HEADERS */ static char s_inetaddr[] = "inet:string->address"; SCM l_inetaddr (host) SCM host; { struct in_addr soka; ASRTER(NIMP(host) && STRINGP(host), host, ARG1, s_inetaddr); soka.s_addr = inet_addr(CHARS(host)); if (-1==soka.s_addr) { struct hostent *entry; DEFER_INTS; SYSCALL(entry = gethostbyname(CHARS(host));); ALLOW_INTS; if (!entry) return BOOL_F; return ulong2num(ntohl(((struct in_addr *)entry->h_addr)->s_addr)); } return ulong2num(ntohl(soka.s_addr)); } static char s_inetstr[] = "inet:address->string"; SCM l_inetstr (inetid) SCM inetid; { struct in_addr addr; char *ans; addr.s_addr = htonl(num2ulong(inetid, (char *)ARG1, s_inetstr)); SYSCALL(ans = inet_ntoa(addr);); return makfrom0str(ans); } static char s_network[] = "inet:network"; SCM l_network (host) SCM host; { struct in_addr addr; addr.s_addr = htonl(num2ulong(host, (char *)ARG1, s_network)); return ulong2num(0L+inet_netof(addr)); } #ifndef __CYGWIN__ static char s_lna[] = "inet:local-network-address"; SCM l_lna (host) SCM host; { struct in_addr addr; addr.s_addr = htonl(num2ulong(host, (char *)ARG1, s_lna)); return ulong2num(0L+inet_lnaof(addr)); } #endif static char s_makaddr[] = "inet:make-address"; SCM l_makaddr (net, lna) SCM net, lna; { struct in_addr addr; unsigned long netnum = num2ulong(net, (char *)ARG1, s_makaddr); unsigned long lnanum = num2ulong(lna, (char *)ARG2, s_makaddr); addr = inet_makeaddr(netnum, lnanum); return ulong2num(ntohl(addr.s_addr)); } #ifndef __CYGWIN__ static char s_hostinfo[] = "gethost"; SCM l_hostinfo(name) SCM name; { SCM ans = make_vector(MAKINUM(5), UNSPECIFIED); SCM *ve = VELTS(ans); SCM lst = EOL; struct hostent *entry; struct in_addr inad; const char **argv; int i = 0; # ifndef linux if (UNBNDP(name)) { DEFER_INTS; SYSCALL(entry = gethostent();); } else # endif if (NIMP(name) && STRINGP(name)) { DEFER_INTS; SYSCALL(entry = gethostbyname(CHARS(name));); } else { inad.s_addr = htonl(num2ulong(name, (char *)ARG1, s_hostinfo)); DEFER_INTS; SYSCALL(entry = gethostbyaddr((char *)&inad , sizeof(inad), AF_INET);); } ALLOW_INTS; if (!entry) return BOOL_F; ve[ 0] = makfrom0str(entry->h_name); ve[ 1] = makfromstrs(-1, entry->h_aliases); ve[ 2] = MAKINUM(entry->h_addrtype + 0L); ve[ 3] = MAKINUM(entry->h_length + 0L); if (sizeof(struct in_addr) != entry->h_length) {ve[ 4] = BOOL_F; return ans;} for (argv = entry->h_addr_list; argv[i]; i++); while (i--) { inad = *(struct in_addr *)argv[i]; lst = cons(ulong2num(ntohl(inad.s_addr)), lst); } ve[ 4] = lst; return ans; } static char s_netinfo[] = "getnet"; SCM l_netinfo(name) SCM name; { SCM ans = make_vector(MAKINUM(4), UNSPECIFIED); SCM *ve = VELTS(ans); struct netent *entry; if (UNBNDP(name)) { DEFER_INTS; SYSCALL(entry = getnetent();); } else if (NIMP(name) && STRINGP(name)) { DEFER_INTS; SYSCALL(entry = getnetbyname(CHARS(name));); } else { unsigned long netnum; netnum = num2ulong(name, (char *)ARG1, s_netinfo); DEFER_INTS; SYSCALL(entry = getnetbyaddr(netnum, AF_INET);); } ALLOW_INTS; if (!entry) return BOOL_F; ve[ 0] = makfrom0str(entry->n_name); ve[ 1] = makfromstrs(-1, entry->n_aliases); ve[ 2] = MAKINUM(entry->n_addrtype + 0L); ve[ 3] = ulong2num(entry->n_net + 0L); return ans; } #endif static char s_protoinfo[] = "getproto"; SCM l_protoinfo(name) SCM name; { SCM ans = make_vector(MAKINUM(3), UNSPECIFIED); SCM *ve = VELTS(ans); struct protoent *entry; if (UNBNDP(name)) { DEFER_INTS; SYSCALL(entry = getprotoent();); } else if (NIMP(name) && STRINGP(name)) { DEFER_INTS; SYSCALL(entry = getprotobyname(CHARS(name));); } else { unsigned long protonum; protonum = num2ulong(name, (char *)ARG1, s_protoinfo); DEFER_INTS; SYSCALL(entry = getprotobynumber(protonum);); } ALLOW_INTS; if (!entry) return BOOL_F; ve[ 0] = makfrom0str(entry->p_name); ve[ 1] = makfromstrs(-1, entry->p_aliases); ve[ 2] = MAKINUM(entry->p_proto + 0L); return ans; } static char s_servinfo[] = "getserv"; SCM l_servinfo(args) SCM args; { SCM ans = make_vector(MAKINUM(4), UNSPECIFIED); SCM *ve = VELTS(ans); SCM name, proto; struct servent *entry; if (NULLP(args)) { DEFER_INTS; SYSCALL(entry = getservent();); goto comlab; } name = CAR(args); proto = CDR(args); ASRTER(NIMP(proto) && CONSP(proto), args, WNA, s_servinfo); proto = CAR(proto); ASRTER(NIMP(proto) && STRINGP(proto), args, ARG2, s_servinfo); DEFER_INTS; if (NIMP(name) && STRINGP(name)) { SYSCALL(entry = getservbyname(CHARS(name), CHARS(proto));); } else { ASRTER(INUMP(proto), proto, ARG1, s_servinfo); SYSCALL(entry = getservbyport(INUM(proto), CHARS(proto));); } comlab: ALLOW_INTS; if (!entry) return BOOL_F; ve[ 0] = makfrom0str(entry->s_name); ve[ 1] = makfromstrs(-1, entry->s_aliases); ve[ 2] = MAKINUM(ntohs(entry->s_port) + 0L); ve[ 3] = makfrom0str(entry->s_proto); return ans; } SCM l_sethost(arg) SCM arg; { if (UNBNDP(arg)) endhostent(); else sethostent(NFALSEP(arg)); return UNSPECIFIED; } #ifndef __CYGWIN__ SCM l_setnet(arg) SCM arg; { if (UNBNDP(arg)) endnetent(); else setnetent(NFALSEP(arg)); return UNSPECIFIED; } #endif SCM l_setproto(arg) SCM arg; { if (UNBNDP(arg)) endprotoent(); else setprotoent(NFALSEP(arg)); return UNSPECIFIED; } SCM l_setserv(arg) SCM arg; { if (UNBNDP(arg)) endservent(); else setservent(NFALSEP(arg)); return UNSPECIFIED; } static char s_socket[] = "make-stream-socket"; SCM l_socket(fam, proto) SCM fam, proto; { int sd, j, tp = INUM(fam); FILE* f; SCM port; ASRTER(INUMP(fam), fam, ARG1, s_socket); if (UNBNDP(proto)) proto = INUM0; else ASRTER(INUMP(proto), proto, ARG2, s_socket); NEWCELL(port); DEFER_INTS; SYSCALL(sd = socket(tp, SOCK_STREAM, INUM(proto));); if (-1==sd) wta(UNDEFINED, (char *)NALLOC, s_socket); SYSCALL(f = fdopen(sd, "r+");); /* SCM_OPENCALL(f = fdopen(sd, "r+")); */ if (!f) { close(sd); wta(MAKINUM(sd), (char *)NALLOC, s_port_type); } port = scm_port_entry(f, tc_socket, BUF0); SCM_PORTDATA(port) = fam; i_setbuf0(port); ALLOW_INTS; if (AF_INET==tp) { #ifdef macintosh sd = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&j, sizeof(j)); #else sd = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &j, sizeof(j)); #endif ASRTER(!sd, port, "could not set socket option", s_socket); } return port; } static char s_socketpair[] = "make-stream-socketpair"; SCM l_socketpair(fam, proto) SCM fam, proto; { int sts, tp = INUM(fam); int sv[2]; FILE* f[2]; SCM port[2]; ASRTER(INUMP(fam), fam, ARG1, s_socketpair); if (UNBNDP(proto)) proto = INUM0; else ASRTER(INUMP(proto), proto, ARG2, s_socketpair); NEWCELL(port[0]); NEWCELL(port[1]); DEFER_INTS; SYSCALL(sts = socketpair(tp, SOCK_STREAM, INUM(proto), sv);); if (-1==sts) wta(UNDEFINED, (char *)NALLOC, s_socketpair); SCM_OPENCALL(f[0] = fdopen(sv[0], "r+")); if (!f[0]) { close(sv[0]); wta(MAKINUM(sv[0]), (char *)NALLOC, s_port_type); } SCM_OPENCALL(f[1] = fdopen(sv[1], "r+")); if (!f[1]) { fclose(f[0]); close(sv[1]); wta(MAKINUM(sv[1]), (char *)NALLOC, s_port_type); } port[0] = scm_port_entry(f[0], tc16_fport, mode_bits("r+0", (char *)0)); CAR(port[1]) = scm_port_entry(f[1], tc16_fport, mode_bits("r+0", (char *)0)); i_setbuf0(port[0]); i_setbuf0(port[1]); ALLOW_INTS; return cons(port[0], port[1]); } static char s_shutdown[] = "socket:shutdown"; SCM l_shutdown(port, how) SCM port, how; { int sts; ASRTER(NIMP(port) && OPFPORTP(port), port, ARG1, s_shutdown); ASRTER(INUMP(how) && 0 <= INUM(how) && 2 >= INUM(how), how, ARG2, s_shutdown); SYSCALL(sts = shutdown(fileno(STREAM(port)), INUM(how));); if (sts) return BOOL_F; switch (INUM(how)) { case 0: CAR(port) &= ~RDNG; break; case 1: CAR(port) &= ~WRTNG; break; case 2: CAR(port) &= ~(RDNG | WRTNG); } if (SOCKP(port)) close_port(port); /* can't read or write */ return port; } static char s_unkfam[] = "unknown-family"; static char s_connect[] = "socket:connect"; SCM l_connect (sockpt, address, arg) SCM sockpt, address, arg; { long flags; int sts; ASRTER(NIMP(sockpt) && SOCKP(sockpt), sockpt, ARG1, s_connect); switch SOCKTYP(sockpt) { default: ASRTER(0, sockpt, s_unkfam, s_connect); case AF_INET: ASRTER(NIMP(arg) && CONSP(arg) && NULLP(CDR(arg)), arg, WNA, s_connect); arg = CAR(arg); ASRTER(INUMP(arg), arg, ARG3, s_connect); { struct sockaddr_in soka; soka.sin_addr.s_addr = htonl(num2ulong(address, (char *)ARG2, s_connect)); soka.sin_family = AF_INET; soka.sin_port = htons(INUM(arg)); SYSCALL(sts = connect(fileno(STREAM(sockpt)), (struct sockaddr*)&soka, sizeof(soka));); } break; case AF_UNIX: ASRTER(NULLP(arg), arg, WNA, s_connect); ASRTER(NIMP(address) && STRINGP(address), address, ARG2, s_connect); { struct sockaddr_un soka; soka.sun_family = AF_UNIX; memcpy(&soka.sun_path, CHARS(address), 1+LENGTH(address)); SYSCALL(sts = connect(fileno(STREAM(sockpt)), (struct sockaddr*)&soka, sizeof(soka));); } break; } if (sts) return BOOL_F; flags = tc16_fport | mode_bits("r+0", (char *)0); SCM_PORTFLAGS(sockpt) = flags; SCM_SETFLAGS(sockpt, flags); SCM_PORTDATA(sockpt) = cons(address, arg); return sockpt; } static char s_bind[] = "socket:bind"; SCM l_bind(sockpt, address) SCM sockpt, address; { int sts; ASRTER(NIMP(sockpt) && SOCKP(sockpt), sockpt, ARG1, s_bind); switch SOCKTYP(sockpt) { default: ASRTER(0, sockpt, s_unkfam, s_bind); case AF_UNIX: ASRTER(NIMP(address) && STRINGP(address), address, ARG2, s_bind); { struct sockaddr_un sa_server; bzero((char *) &sa_server, sizeof(sa_server)); sa_server.sun_family = AF_UNIX; memcpy(&sa_server.sun_path, CHARS(address), 1+LENGTH(address)); SYSCALL(sts = bind(fileno(STREAM(sockpt)), (struct sockaddr *)&sa_server, sizeof(sa_server));); } break; case AF_INET: ASRTER(INUMP(address), address, ARG2, s_bind); { struct sockaddr_in sa_server; bzero((char *) &sa_server, sizeof(sa_server)); sa_server.sin_family = AF_INET; sa_server.sin_addr.s_addr = htonl(INADDR_ANY); sa_server.sin_port = htons(INUM(address)); SYSCALL(sts = bind(fileno(STREAM(sockpt)), (struct sockaddr *)&sa_server, sizeof(sa_server));); } break; } return sts ? BOOL_F : sockpt; } static char s_listen[] = "socket:listen"; SCM l_listen(port, backlog) SCM port, backlog; { long flags; int sts; ASRTER(NIMP(port) && SOCKP(port), port, ARG1, s_listen); ASRTER(INUMP(backlog), backlog, ARG2, s_listen); SYSCALL(sts = listen(fileno(STREAM(port)), INUM(backlog));); if (sts) return BOOL_F; DEFER_INTS; flags = tc16_fport | mode_bits("r0", (char *)0); SCM_PORTFLAGS(port) = flags; SCM_SETFLAGS(port, flags); ALLOW_INTS; return port; } static char s_accept[] = "socket:accept"; SCM l_accept(sockpt) SCM sockpt; { int newsd, sadlen; struct sockaddr sad; FILE *newfd; SCM newpt; NEWCELL(newpt); ASRTER(NIMP(sockpt) && OPINPORTP(sockpt), sockpt, ARG1, s_accept); sadlen=sizeof(sad); SYSCALL(newsd = accept(fileno(STREAM(sockpt)), &sad, &sadlen);); if (-1==newsd) { #ifndef macintosh if (EWOULDBLOCK != errno) return BOOL_F; else #endif wta(sockpt, "couldn't", s_accept); } DEFER_INTS; SCM_OPENCALL(newfd = fdopen(newsd, "r+")); if (!newfd) { close(newsd); wta(MAKINUM(newsd), (char *)NALLOC, s_port_type); } newpt = scm_port_entry(newfd, tc16_fport, mode_bits("r+0", (char *)0)); i_setbuf0(newpt); ALLOW_INTS; return newpt; } int sknm_print(exp, port, writing) SCM exp; SCM port; int writing; { lputs("#<", port); switch (((struct sockaddr *)CDR(exp))->sa_family) { case AF_UNIX: lputs("unix-addr ", port); lputs(((struct sockaddr_un *)CDR(exp))->sun_path, port); break; case AF_INET: lputs("inet-addr ", port); lputs(inet_ntoa(((struct sockaddr_in *)CDR(exp))->sin_addr), port); lputc(':', port); scm_intprint(0L + ntohs(((struct sockaddr_in *)CDR(exp))->sin_port), 10, port); break; default: lputs(s_unkfam, port); lputc(' ', port); scm_intprint(0L+((struct sockaddr *)CDR(exp))->sa_family, 10, port); } lputc('>', port); return !0; } sizet sknm_free(p) CELLPTR p; { must_free(CHARS((SCM)p), sizeof(struct sockaddr)); return 0; } long tc16_sknm; static smobfuns sknm_smob = {mark0, sknm_free, sknm_print, 0}; char s_sknm_family[] = "socket-name:family"; SCM l_sknm_family(snm) SCM snm; { ASRTER(NIMP(snm) && TYP16(snm)==tc16_sknm, snm, ARG1, s_sknm_family); return MAKINUM(((struct sockaddr *)CDR(snm))->sa_family + 0L); } char s_sknm_port_num[] = "socket-name:port-number"; SCM l_sknm_port_num(snm) SCM snm; { ASRTGO(NIMP(snm) && TYP16(snm)==tc16_sknm, err1); switch (((struct sockaddr *)CDR(snm))->sa_family) { default: err1: wta(snm, (char *)ARG1, s_sknm_port_num); case AF_INET: return MAKINUM(ntohs(((struct sockaddr_in *)CDR(snm))->sin_port) + 0L); } } char s_sknm_addr[] = "socket-name:address"; SCM l_sknm_addr(snm) SCM snm; { ASRTGO(NIMP(snm) && TYP16(snm)==tc16_sknm, err1); switch (((struct sockaddr *)CDR(snm))->sa_family) { default: err1: wta(snm, (char *)ARG1, s_sknm_addr); case AF_INET: return ulong2num(ntohl(((struct sockaddr_in *)CDR(snm))->sin_addr.s_addr)); case AF_UNIX: /* the manual says this won't work anyway */ return makfrom0str(((struct sockaddr_un *)CDR(snm))->sun_path); } } SCM maksknm(sad) struct sockaddr *sad; { SCM sknm; struct sockaddr *msknm; DEFER_INTS; sknm = must_malloc_cell(0L+sizeof(struct sockaddr), (SCM)tc16_sknm, "sknm"); msknm = (struct sockaddr *)CDR(sknm); *msknm = *sad; ALLOW_INTS; return sknm; } static char s_getpeername[] = "getpeername"; SCM l_getpeername(sockpt) SCM sockpt; { struct sockaddr_in sad; int sts, sadlen = sizeof(sad); bzero((char *) &sad, sizeof(sad)); ASRTER(NIMP(sockpt) && OPFPORTP(sockpt), sockpt, ARG1, s_getpeername); SYSCALL(sts = getpeername(fileno(STREAM(sockpt)), (struct sockaddr*)&sad, &sadlen);); if (sts || sizeof(sad) != sadlen) return BOOL_F; /* ASRTER(sad.sin_family==AF_INET, sockpt, "non-internet", s_getpeername); */ return maksknm(&sad); } static char s_getsockname[] = "getsockname"; SCM l_getsockname(sockpt) SCM sockpt; { struct sockaddr_in sad; int sts, sadlen = sizeof(sad); bzero((char *) &sad, sizeof(sad)); ASRTER(NIMP(sockpt) && OPFPORTP(sockpt), sockpt, ARG1, s_getsockname); SYSCALL(sts = getsockname(fileno(STREAM(sockpt)), (struct sockaddr*)&sad, &sadlen);); if (sts || sizeof(sad) != sadlen) return BOOL_F; return maksknm(&sad); } static iproc subr1s[] = { {s_inetaddr, l_inetaddr}, {s_inetstr, l_inetstr}, {s_network, l_network}, #ifndef __CYGWIN__ {s_lna, l_lna}, #endif {s_accept, l_accept}, {s_sknm_family, l_sknm_family}, {s_sknm_port_num, l_sknm_port_num}, {s_sknm_addr, l_sknm_addr}, {s_getpeername, l_getpeername}, {s_getsockname, l_getsockname}, {0, 0}}; static iproc subr1os[] = { {s_protoinfo, l_protoinfo}, #ifndef __CYGWIN__ {s_hostinfo, l_hostinfo}, {s_netinfo, l_netinfo}, {"setnetent", l_setnet}, #endif {"sethostent", l_sethost}, {"setprotoent", l_setproto}, {"setservent", l_setserv}, {0, 0}}; static iproc subr2s[] = { {s_shutdown, l_shutdown}, {s_bind, l_bind}, {s_listen, l_listen}, {s_makaddr, l_makaddr}, {0, 0}}; void init_socket() { sysintern("af_unix", MAKINUM(AF_UNIX)); sysintern("af_inet", MAKINUM(AF_INET)); init_iprocs(subr1s, tc7_subr_1); init_iprocs(subr1os, tc7_subr_1o); init_iprocs(subr2s, tc7_subr_2); make_subr(s_servinfo, tc7_lsubr, l_servinfo); make_subr(s_socket, tc7_subr_2o, l_socket); make_subr(s_socketpair, tc7_subr_2o, l_socketpair); make_subr(s_connect, tc7_lsubr_2, l_connect); tc16_sknm = newsmob(&sknm_smob); add_feature("socket"); }