int dns_encode_a_response(char *buf, size_t buflen, struct query *q) /* Only used when iodined gets an A type query for ns.topdomain or www.topdomain */ /* Mostly same as dns_encode_ns_response() above */ { HEADER *header; int len; short name; char *ipp; char *p; if (buflen < sizeof(HEADER)) return 0; memset(buf, 0, buflen); header = (HEADER*)buf; header->id = htons(q->id); header->qr = 1; header->opcode = 0; header->aa = 1; header->tc = 0; header->rd = 0; header->ra = 0; p = buf + sizeof(HEADER); header->qdcount = htons(1); header->ancount = htons(1); /* pointer to start of name */ name = 0xc000 | ((p - buf) & 0x3fff); /* Query section */ putname(&p, buflen - (p - buf), q->name); /* Name */ CHECKLEN(4); putshort(&p, q->type); /* Type */ putshort(&p, C_IN); /* Class */ /* Answer section */ CHECKLEN(12); putshort(&p, name); /* Name */ putshort(&p, q->type); /* Type */ putshort(&p, C_IN); /* Class */ putlong(&p, 3600); /* TTL */ putshort(&p, 4); /* Data length */ /* ugly hack to output IP address */ ipp = (char *) &q->destination; CHECKLEN(4); putbyte(&p, *(ipp++)); putbyte(&p, *(ipp++)); putbyte(&p, *(ipp++)); putbyte(&p, *ipp); len = p - buf; return len; }
/* Takes a format argument and a string argument. Interprets data * in the string according to the format specification and returns * that data. * * Format characters: * ? = 1 byte of data to be discarded * b = signed byte value * B = unsigned byte value * c = 1-character string * f = 4-byte floating-point value (float) * h = signed 2-byte value (short) * H = unsigned 2-byte value (short) * i = signed 4-byte value (int) * I = unsigned 4-byte value (int) * l = signed 8-byte value (long long) * L = unsigned 8-byte value (long long) * s = string preceded by length (1 byte) * S = string preceded by length (2 bytes) * t = string preceded by length (4 bytes) * v = vector (3 floats totaling 12 bytes) * z = null-terminated string */ static int unpack(lua_State *L) { int n; size_t len; const char *str; const char *fmt; n = lua_gettop(L); if (n != 2) return luaL_error(L, "expected 2 arguments for binread function (got %d)", n); fmt = luaL_checkstring(L, 1); str = luaL_checklstring(L, 2, &len); DEBUG(3, "unpack(\"%s\", ...)\n", fmt); lua_settop(L, 0); n = 0; for (; *fmt; fmt++) { switch (*fmt) { case '?': CHECKLEN(1); str = &str[1]; n--; len--; break; case 'b': CHECKLEN(1); lua_pushinteger(L, *((signed char *) str)); str = &str[1]; len--; break; case 'B': CHECKLEN(1); lua_pushinteger(L, *((unsigned char *) str)); str = &str[1]; len--; break; case 'c': CHECKLEN(1); { char c[2] = { str[0], '\0' }; lua_pushstring(L, c); } str = &str[1]; len--; break; case 'f': CHECKLEN(4); lua_pushnumber(L, *((float *) str)); str = &str[4]; len -= 4; break; case 'h': CHECKLEN(2); lua_pushinteger(L, *((short *) str)); str = &str[2]; len -= 2; break; case 'H': CHECKLEN(2); lua_pushinteger(L, *((unsigned short *) str)); str = &str[2]; len -= 2; break; case 'i': CHECKLEN(4); lua_pushinteger(L, *((int *) str)); str = &str[4]; len -= 4; break; case 'I': CHECKLEN(4); lua_pushinteger(L, *((unsigned int *) str)); str = &str[4]; len -= 4; break; case 'l': CHECKLEN(8); lua_pushnumber(L, *((long long *) str)); str = &str[8]; len -= 8; break; case 'L': CHECKLEN(8); lua_pushnumber(L, *((unsigned long long *) str)); str = &str[8]; len -= 8; break; case 's': CHECKLEN(1); { unsigned char l = str[0]; str = &str[1]; len -= 1; CHECKLEN(l); lua_pushlstring(L, str, l); str = &str[l]; len -= l; } break; case 'S': CHECKLEN(2); { unsigned short l = *((unsigned short *) str); str = &str[2]; len -= 2; CHECKLEN(l); lua_pushlstring(L, str, l); str = &str[l]; len -= l; } break; case 't': CHECKLEN(4); { unsigned int l = *((unsigned int *) str); str = &str[4]; len -= 4; CHECKLEN(l); lua_pushlstring(L, str, l); str = &str[l]; len -= l; } break; case 'v': CHECKLEN(12); lua_getglobal(L, "vec"); lua_pushnumber(L, *((float *) str)); str = &str[4]; lua_pushnumber(L, *((float *) str)); str = &str[4]; lua_pushnumber(L, *((float *) str)); str = &str[4]; lua_call(L, 3, 1); len -= 12; break; case 'z': CHECKLEN(1); { int l = strlen(str); lua_pushstring(L, str); str = &str[l]; len -= l; } break; default: luaL_error(L, "invalid character '%c' in format string", *fmt); } n++; } return n; }
void iap_handlepkt_mode2(const unsigned int len, const unsigned char *buf) { static bool poweron_pressed = false; unsigned int cmd = buf[1]; /* We expect at least three bytes in the buffer, one for the * lingo, one for the command, and one for the first button * state bits. */ CHECKLEN(3); /* Lingo 0x02 must have been negotiated */ if (!DEVICE_LINGO_SUPPORTED(0x02)) { cmd_ack(cmd, IAP_ACK_BAD_PARAM); return; } switch (cmd) { /* ContextButtonStatus (0x00) * * Transmit button events from the device to the iPod * * Packet format (offset in buf[]: Description) * 0x00: Lingo ID: Simple Remote Lingo, always 0x02 * 0x01: Command, always 0x00 * 0x02: Button states 0:7 * 0x03: Button states 8:15 (optional) * 0x04: Button states 16:23 (optional) * 0x05: Button states 24:31 (optional) * * Returns: (none) */ case 0x00: { iap_remotebtn = BUTTON_NONE; iap_timeoutbtn = 0; if(buf[2] != 0) { if(buf[2] & 1) REMOTE_BUTTON(BUTTON_RC_PLAY); if(buf[2] & 2) REMOTE_BUTTON(BUTTON_RC_VOL_UP); if(buf[2] & 4) REMOTE_BUTTON(BUTTON_RC_VOL_DOWN); if(buf[2] & 8) REMOTE_BUTTON(BUTTON_RC_RIGHT); if(buf[2] & 16) REMOTE_BUTTON(BUTTON_RC_LEFT); } else if(len >= 4 && buf[3] != 0) { if(buf[3] & 1) /* play */ { if (audio_status() != AUDIO_STATUS_PLAY) REMOTE_BUTTON(BUTTON_RC_PLAY); } if(buf[3] & 2) /* pause */ { if (audio_status() == AUDIO_STATUS_PLAY) REMOTE_BUTTON(BUTTON_RC_PLAY); } if(buf[3] & 128) /* Shuffle */ { if (!iap_btnshuffle) { iap_shuffle_state(!global_settings.playlist_shuffle); iap_btnshuffle = true; } } } else if(len >= 5 && buf[4] != 0) { if(buf[4] & 1) /* repeat */ { if (!iap_btnrepeat) { iap_repeat_next(); iap_btnrepeat = true; } } if (buf[4] & 2) /* power on */ { poweron_pressed = true; } /* Power off * Not quite sure how to react to this, but stopping playback * is a good start. */ if (buf[4] & 0x04) { if (audio_status() == AUDIO_STATUS_PLAY) REMOTE_BUTTON(BUTTON_RC_PLAY); } if(buf[4] & 16) /* ffwd */ REMOTE_BUTTON(BUTTON_RC_RIGHT); if(buf[4] & 32) /* frwd */ REMOTE_BUTTON(BUTTON_RC_LEFT); } /* power on released */ if (poweron_pressed && len >= 5 && !(buf[4] & 2)) { poweron_pressed = false; #ifdef HAVE_LINE_REC /* Belkin TuneTalk microphone sends power-on press+release * events once authentication sequence is finished, * GetDevCaps command is ignored by the device when it is * sent before power-on release event is received. * XXX: It is unknown if other microphone devices are * sending the power-on events. */ if (DEVICE_LINGO_SUPPORTED(0x01)) { /* GetDevCaps */ IAP_TX_INIT(0x01, 0x07); iap_send_tx(); } #endif } break; } /* ACK (0x01) * * Sent from the iPod to the device */ /* ImageButtonStatus (0x02) * * Transmit image button events from the device to the iPod * * Packet format (offset in buf[]: Description) * 0x00: Lingo ID: Simple Remote Lingo, always 0x02 * 0x01: Command, always 0x02 * 0x02: Button states 0:7 * 0x03: Button states 8:15 (optional) * 0x04: Button states 16:23 (optional) * 0x05: Button states 24:31 (optional) * * This command requires authentication * * Returns on success: * IAP_ACK_OK * * Returns on failure: * IAP_ACK_* */ case 0x02: { if (!DEVICE_AUTHENTICATED) { cmd_ack(cmd, IAP_ACK_NO_AUTHEN); break; } cmd_ack(cmd, IAP_ACK_CMD_FAILED); break; } /* VideoButtonStatus (0x03) * * Transmit video button events from the device to the iPod * * Packet format (offset in buf[]: Description) * 0x00: Lingo ID: Simple Remote Lingo, always 0x02 * 0x01: Command, always 0x03 * 0x02: Button states 0:7 * 0x03: Button states 8:15 (optional) * 0x04: Button states 16:23 (optional) * 0x05: Button states 24:31 (optional) * * This command requires authentication * * Returns on success: * IAP_ACK_OK * * Returns on failure: * IAP_ACK_* */ case 0x03: { if (!DEVICE_AUTHENTICATED) { cmd_ack(cmd, IAP_ACK_NO_AUTHEN); break; } cmd_ack(cmd, IAP_ACK_CMD_FAILED); break; } /* AudioButtonStatus (0x04) * * Transmit audio button events from the device to the iPod * * Packet format (offset in buf[]: Description) * 0x00: Lingo ID: Simple Remote Lingo, always 0x02 * 0x01: Command, always 0x04 * 0x02: Button states 0:7 * 0x03: Button states 8:15 (optional) * 0x04: Button states 16:23 (optional) * 0x05: Button states 24:31 (optional) * * This command requires authentication * * Returns on success: * IAP_ACK_OK * * Returns on failure: * IAP_ACK_* */ case 0x04: { unsigned char repeatbuf[6]; if (!DEVICE_AUTHENTICATED) { cmd_ack(cmd, IAP_ACK_NO_AUTHEN); break; } /* This is basically the same command as ContextButtonStatus (0x00), * with the difference that it requires authentication and that * it returns an ACK packet to the device. * So just route it through the handler again, with 0x00 as the * command */ memcpy(repeatbuf, buf, 6); repeatbuf[1] = 0x00; iap_handlepkt_mode2((len<6)?len:6, repeatbuf); cmd_ok(cmd); break; } /* The default response is IAP_ACK_BAD_PARAM */ default: { #ifdef LOGF_ENABLE logf("iap: Unsupported Mode02 Command"); #else cmd_ack(cmd, IAP_ACK_BAD_PARAM); #endif break; } } }
int dns_encode(char *buf, size_t buflen, struct query *q, qr_t qr, char *data, size_t datalen) { HEADER *header; short name; char *p; int len; int ancnt; if (buflen < sizeof(HEADER)) return 0; memset(buf, 0, buflen); header = (HEADER*)buf; header->id = htons(q->id); header->qr = (qr == QR_ANSWER); header->opcode = 0; header->aa = (qr == QR_ANSWER); header->tc = 0; header->rd = (qr == QR_QUERY); header->ra = 0; p = buf + sizeof(HEADER); switch (qr) { case QR_ANSWER: header->qdcount = htons(1); name = 0xc000 | ((p - buf) & 0x3fff); /* Question section */ putname(&p, buflen - (p - buf), q->name); CHECKLEN(4); putshort(&p, q->type); putshort(&p, C_IN); /* Answer section */ if (q->type == T_CNAME || q->type == T_A || q->type == T_PTR || q->type == T_AAAA || q->type == T_A6 || q->type == T_DNAME) { /* data is expected to be like "Hblabla.host.name.com\0" */ char *startp; int namelen; CHECKLEN(10); putshort(&p, name); if (q->type == T_A || q->type == T_AAAA) /* answer CNAME to A question */ putshort(&p, T_CNAME); else putshort(&p, q->type); putshort(&p, C_IN); putlong(&p, 0); /* TTL */ startp = p; p += 2; /* skip 2 bytes length */ if (q->type == T_A6) { CHECKLEN(1); putbyte(&p, 128); } putname(&p, buflen - (p - buf), data); CHECKLEN(0); namelen = p - startp; namelen -= 2; putshort(&startp, namelen); ancnt = 1; } else if (q->type == T_MX || q->type == T_SRV) { /* Data is expected to be like "Hblabla.host.name.com\0Hanother.com\0\0" For SRV, see RFC2782. */ char *mxdata = data; char *startp; int namelen; ancnt = 1; while (1) { CHECKLEN(10); putshort(&p, name); putshort(&p, q->type); putshort(&p, C_IN); putlong(&p, 0); /* TTL */ startp = p; p += 2; /* skip 2 bytes length */ CHECKLEN(2); putshort(&p, 10 * ancnt); /* preference */ if (q->type == T_SRV) { /* weight, port (5060 = SIP) */ CHECKLEN(4); putshort(&p, 10); putshort(&p, 5060); } putname(&p, buflen - (p - buf), mxdata); CHECKLEN(0); namelen = p - startp; namelen -= 2; putshort(&startp, namelen); mxdata = mxdata + strlen(mxdata) + 1; if (*mxdata == '\0') break; ancnt++; } } else if (q->type == T_TXT) { /* TXT has binary or base-X data */ char *startp; int txtlen; CHECKLEN(10); putshort(&p, name); putshort(&p, q->type); putshort(&p, C_IN); putlong(&p, 0); /* TTL */ startp = p; p += 2; /* skip 2 bytes length */ puttxtbin(&p, buflen - (p - buf), data, datalen); CHECKLEN(0); txtlen = p - startp; txtlen -= 2; putshort(&startp, txtlen); ancnt = 1; } else { /* NULL has raw binary data */ CHECKLEN(10); putshort(&p, name); putshort(&p, q->type); putshort(&p, C_IN); putlong(&p, 0); /* TTL */ datalen = MIN(datalen, buflen - (p - buf)); CHECKLEN(2); putshort(&p, datalen); CHECKLEN(datalen); putdata(&p, data, datalen); CHECKLEN(0); ancnt = 1; } header->ancount = htons(ancnt); break; case QR_QUERY: /* Note that iodined also uses this for forward queries */ header->qdcount = htons(1); datalen = MIN(datalen, buflen - (p - buf)); putname(&p, datalen, data); CHECKLEN(4); putshort(&p, q->type); putshort(&p, C_IN); /* EDNS0 to advertise maximum response length (even CNAME/A/MX, 255+255+header would be >512) */ if (dnsc_use_edns0) { header->arcount = htons(1); CHECKLEN(11); putbyte(&p, 0x00); /* Root */ putshort(&p, 0x0029); /* OPT */ putshort(&p, 0x1000); /* Payload size: 4096 */ putshort(&p, 0x0000); /* Higher bits/edns version */ putshort(&p, 0x8000); /* Z */ putshort(&p, 0x0000); /* Data length */ } break; } len = p - buf; return len; }
int dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, size_t packetlen) { char name[QUERY_NAME_SIZE]; char rdata[4*1024]; HEADER *header; short qdcount; short ancount; uint32_t ttl; unsigned short class; unsigned short type; char *data; unsigned short rlen; uint16_t id; int rv; rv = 0; header = (HEADER*)packet; /* Reject short packets */ if (packetlen < sizeof(HEADER)) return 0; if (header->qr != qr) { warnx("header->qr does not match the requested qr"); return -1; } data = packet + sizeof(HEADER); qdcount = ntohs(header->qdcount); ancount = ntohs(header->ancount); id = ntohs(header->id); rlen = 0; if (q != NULL) q->rcode = header->rcode; switch (qr) { case QR_ANSWER: if(qdcount < 1) { /* We need a question */ return -1; } if (q != NULL) q->id = id; /* Read name even if no answer, to give better error message */ readname(packet, packetlen, &data, name, sizeof(name)); CHECKLEN(4); readshort(packet, &data, &type); readshort(packet, &data, &class); /* if CHECKLEN okay, then we're sure to have a proper name */ if (q != NULL) { /* We only need the first char to check it */ q->name[0] = name[0]; q->name[1] = '\0'; } if (ancount < 1) { /* DNS errors like NXDOMAIN have ancount=0 and stop here. CNAME may also have A; MX/SRV may have multiple results. */ return -1; } /* Here type is still the question type */ if (type == T_NULL || type == T_PRIVATE) { /* Assume that first answer is what we wanted */ readname(packet, packetlen, &data, name, sizeof(name)); CHECKLEN(10); readshort(packet, &data, &type); readshort(packet, &data, &class); readlong(packet, &data, &ttl); readshort(packet, &data, &rlen); rv = MIN(rlen, sizeof(rdata)); rv = readdata(packet, &data, rdata, rv); if (rv >= 2 && buf) { rv = MIN(rv, buflen); memcpy(buf, rdata, rv); } else { rv = 0; } } else if ((type == T_A || type == T_CNAME ||
int dns_encode_ns_response(char *buf, size_t buflen, struct query *q, char *topdomain) /* Only used when iodined gets an NS type query */ /* Mostly same as dns_encode_a_response() below */ { HEADER *header; int len; short name; short topname; short nsname; char *ipp; int domain_len; char *p; if (buflen < sizeof(HEADER)) return 0; memset(buf, 0, buflen); header = (HEADER*)buf; header->id = htons(q->id); header->qr = 1; header->opcode = 0; header->aa = 1; header->tc = 0; header->rd = 0; header->ra = 0; p = buf + sizeof(HEADER); header->qdcount = htons(1); header->ancount = htons(1); header->arcount = htons(1); /* pointer to start of name */ name = 0xc000 | ((p - buf) & 0x3fff); domain_len = strlen(q->name) - strlen(topdomain); if (domain_len < 0 || domain_len == 1) return -1; if (strcasecmp(q->name + domain_len, topdomain)) return -1; if (domain_len >= 1 && q->name[domain_len - 1] != '.') return -1; /* pointer to start of topdomain; instead of dots at the end we have length-bytes in front, so total length is the same */ topname = 0xc000 | ((p - buf + domain_len) & 0x3fff); /* Query section */ putname(&p, buflen - (p - buf), q->name); /* Name */ CHECKLEN(4); putshort(&p, q->type); /* Type */ putshort(&p, C_IN); /* Class */ /* Answer section */ CHECKLEN(12); putshort(&p, name); /* Name */ putshort(&p, q->type); /* Type */ putshort(&p, C_IN); /* Class */ putlong(&p, 3600); /* TTL */ putshort(&p, 5); /* Data length */ /* pointer to ns.topdomain */ nsname = 0xc000 | ((p - buf) & 0x3fff); CHECKLEN(5); putbyte(&p, 2); putbyte(&p, 'n'); putbyte(&p, 's'); putshort(&p, topname); /* Name Server */ /* Additional data (A-record of NS server) */ CHECKLEN(12); putshort(&p, nsname); /* Name Server */ putshort(&p, T_A); /* Type */ putshort(&p, C_IN); /* Class */ putlong(&p, 3600); /* TTL */ putshort(&p, 4); /* Data length */ /* ugly hack to output IP address */ ipp = (char *) &q->destination; CHECKLEN(4); putbyte(&p, *(ipp++)); putbyte(&p, *(ipp++)); putbyte(&p, *(ipp++)); putbyte(&p, *ipp); len = p - buf; return len; }