#include #include #include #include #include #include #include #include "ipc.h" static void putlen(unsigned char p[Ipclensz], size_t n) { p[0] = n; p[1] = n >> 8; } static size_t getlen(const unsigned char p[Ipclensz]) { return p[0] | (p[1] << 8); } static void put32(unsigned char *p, int32_t v) { uint32_t u; u = v; p[0] = u; p[1] = u >> 8; p[2] = u >> 16; p[3] = u >> 24; } static int32_t get32(const unsigned char *p) { uint32_t u; u = (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24); if(u <= INT32_MAX) return u; return -(int32_t)(~u) - 1; } int ipcpath(char *dst, size_t cap) { const char *dir, *sep; int n; if(dst == NULL || cap == 0) return -1; dir = getenv("XDG_RUNTIME_DIR"); if(dir != NULL && dir[0] == '/'){ sep = dir[strlen(dir)-1] == '/' ? "" : "/"; n = snprintf(dst, cap, "%s%sstrans.sock", dir, sep); }else n = snprintf(dst, cap, "/tmp/strans.%lu", (unsigned long)getuid()); if(n < 0 || (size_t)n >= cap) return -1; return 0; } int ipcconnect(void) { struct sockaddr_un addr; int e, fd; memset(&addr, 0, sizeof addr); addr.sun_family = AF_UNIX; if(ipcpath(addr.sun_path, sizeof addr.sun_path) < 0){ errno = ENAMETOOLONG; return -1; } fd = socket(AF_UNIX, SOCK_STREAM, 0); if(fd < 0) return -1; if(connect(fd, (struct sockaddr*)&addr, sizeof addr) == 0) return fd; e = errno; close(fd); errno = e; return -1; } void ipcpackreq(unsigned char req[Ipcreqsz], int want, uint32_t mod, uint32_t key) { req[0] = want ? Ipcreqwant : 0; req[1] = mod; req[2] = key; req[3] = key >> 8; req[4] = key >> 16; req[5] = key >> 24; } void ipcpackreset(unsigned char req[Ipcreqsz], int want) { memset(req, 0, Ipcreqsz); req[0] = Ipcreqreset | (want ? Ipcreqwant : 0); } /* An old server sees this reserved frame as a key-zero request. */ void ipcpackcap(unsigned char req[Ipcreqsz], int want) { memset(req, 0, Ipcreqsz); req[0] = Ipcext | (want ? Ipcreqwant : 0); req[1] = Ipcversion; req[2] = Ipcopcap; } int ipcpackcaret(unsigned char req[Ipccaretsz], int valid, int32_t x, int32_t y, int32_t h) { if(valid != 0 && valid != 1) return -1; if(valid && h < 0) return -1; memset(req, 0, Ipccaretsz); req[0] = Ipcext; req[1] = Ipcversion; req[2] = Ipcopcaret; req[3] = valid; if(!valid) return 0; put32(req + 4, x); put32(req + 8, y); put32(req + 12, h); return 0; } void ipcunpackreq(const unsigned char req[Ipcreqsz], int *want, uint32_t *mod, uint32_t *key) { *want = (req[0] & Ipcreqwant) != 0; *mod = req[1] & Mmask; *key = (uint32_t)req[2] | ((uint32_t)req[3] << 8) | ((uint32_t)req[4] << 16) | ((uint32_t)req[5] << 24); } int ipcreqtype(const unsigned char req[Ipcreqsz]) { if((req[0] & Ipcext) == 0) return Ipclegacy; if(req[1] != Ipcversion) return Ipcunknown; switch(req[2]){ case Ipcopcap: if((req[0] & ~(Ipcext|Ipcreqwant)) != 0 || req[3] != 0 || req[4] != 0 || req[5] != 0) return Ipcunknown; return Ipccap; case Ipcopcaret: if(req[0] != Ipcext || req[3] > 1) return Ipcunknown; return Ipccaret; } return Ipcunknown; } int ipcunpackcaret(const unsigned char req[Ipccaretsz], int *valid, int32_t *x, int32_t *y, int32_t *h) { int32_t wireh; if(valid == NULL || x == NULL || y == NULL || h == NULL || ipcreqtype(req) != Ipccaret) return -1; wireh = get32(req + 12); if(wireh < 0) return -1; *valid = req[3]; if(!*valid){ *x = 0; *y = 0; *h = 0; return 0; } *x = get32(req + 4); *y = get32(req + 8); *h = wireh; return 0; } int ipcreqreset(const unsigned char req[Ipcreqsz]) { return (req[0] & Ipcreqreset) != 0; } int ipcpackresp(unsigned char *dst, size_t cap, int eaten, const char *commit, size_t ncommit, const char *preedit, size_t npreedit, int want) { size_t n; if(ncommit > Ipcfieldmax || npreedit > Ipcfieldmax) return -1; if((ncommit > 0 && commit == NULL) || (want && npreedit > 0 && preedit == NULL)) return -1; n = Ipcresphdrsz + ncommit + (want ? Ipclensz + npreedit : 0); if(dst == NULL || cap < n) return -1; dst[0] = eaten != 0; putlen(dst + 1, ncommit); if(ncommit > 0) memcpy(dst + Ipcresphdrsz, commit, ncommit); if(want){ putlen(dst + Ipcresphdrsz + ncommit, npreedit); if(npreedit > 0) memcpy(dst + Ipcresphdrsz + ncommit + Ipclensz, preedit, npreedit); } return n; } int ipcreadn(int fd, void *buf, size_t n) { unsigned char *p; ssize_t r; p = buf; while(n > 0){ r = read(fd, p, n); if(r < 0 && errno == EINTR) continue; if(r <= 0) return -1; p += r; n -= r; } return 0; } int ipcsend(int fd, const void *buf, size_t n) { const unsigned char *p; ssize_t r; p = buf; while(n > 0){ r = send(fd, p, n, MSG_NOSIGNAL); if(r < 0 && errno == EINTR) continue; if(r <= 0) return -1; p += r; n -= r; } return 0; } static int readfield(int fd, size_t n, char *dst, size_t cap) { unsigned char discard[128]; size_t keep, part; if(cap > 0 && dst == NULL) return -1; keep = 0; if(cap > 0) keep = n >= cap ? cap - 1 : n; if(keep > 0 && ipcreadn(fd, dst, keep) < 0) return -1; if(cap > 0) dst[keep] = '\0'; n -= keep; while(n > 0){ part = n < sizeof discard ? n : sizeof discard; if(ipcreadn(fd, discard, part) < 0) return -1; n -= part; } return 0; } int ipcreadresp(int fd, int want, char *commit, size_t ccap, char *preedit, size_t pcap, Ipcresp *resp) { unsigned char hdr[Ipcresphdrsz], npreedit[Ipclensz]; if(resp == NULL || (ccap > 0 && commit == NULL) || (pcap > 0 && preedit == NULL)) return -1; if(ccap > 0) commit[0] = '\0'; if(pcap > 0) preedit[0] = '\0'; memset(resp, 0, sizeof *resp); if(ipcreadn(fd, hdr, sizeof hdr) < 0) return -1; resp->eaten = hdr[0] != 0; resp->ncommit = getlen(hdr + 1); if(resp->ncommit > Ipcfieldmax) return -1; if(readfield(fd, resp->ncommit, commit, ccap) < 0) return -1; if(!want) return 0; if(ipcreadn(fd, npreedit, sizeof npreedit) < 0) return -1; resp->npreedit = getlen(npreedit); if(resp->npreedit > Ipcfieldmax) return -1; return readfield(fd, resp->npreedit, preedit, pcap); }