struct client_cache_s * SearchClientCache(struct in_addr addr, int quiet) { int i; for (i = 0; i < CLIENT_CACHE_SLOTS; i++) { if (clients[i].addr.s_addr == addr.s_addr) { /* Invalidate this client cache if it's older than 1 hour */ if ((time(NULL) - clients[i].age) > 3600) { unsigned char mac[6]; if (get_remote_mac(addr, mac) == 0 && memcmp(mac, clients[i].mac, 6) == 0) { /* Same MAC as last time when we were able to identify the client, * so extend the timeout by another hour. */ clients[i].age = time(NULL); } else { memset(&clients[i], 0, sizeof(struct client_cache_s)); return NULL; } } if (!quiet) DPRINTF(E_DEBUG, L_HTTP, "Client found in cache. [%s/entry %d]\n", clients[i].type->name, i); return &clients[i]; } } return NULL; }
struct client_cache_s * AddClientCache(struct in_addr addr, int type) { int i; for (i = 0; i < CLIENT_CACHE_SLOTS; i++) { if (clients[i].addr.s_addr) continue; get_remote_mac(addr, clients[i].mac); clients[i].addr = addr; clients[i].type = &client_types[type]; clients[i].age = time(NULL); DPRINTF(E_DEBUG, L_HTTP, "Added client [%s/%s/%02X:%02X:%02X:%02X:%02X:%02X] to cache slot %d.\n", client_types[type].name, inet_ntoa(clients[i].addr), clients[i].mac[0], clients[i].mac[1], clients[i].mac[2], clients[i].mac[3], clients[i].mac[4], clients[i].mac[5], i); return &clients[i]; } return NULL; }
void ParseUPnPClient(char *location) { char buf[8192]; struct sockaddr_in dest; int s, n, do_headers = 0, nread = 0; struct timeval tv; char *addr, *path, *port_str; long port = 80; char *off = NULL, *p; int content_len = sizeof(buf); struct NameValueParserData xml; int client; enum client_types type = 0; uint32_t flags = 0; char *model, *serial, *name; if (strncmp(location, "http://", 7) != 0) return; path = location + 7; port_str = strsep(&path, "/"); if (!path) return; addr = strsep(&port_str, ":"); if (port_str) { port = strtol(port_str, NULL, 10); if (!port) port = 80; } memset(&dest, '\0', sizeof(dest)); if (!inet_aton(addr, &dest.sin_addr)) return; /* Check if the client is already in cache */ dest.sin_family = AF_INET; dest.sin_port = htons(port); s = socket(PF_INET, SOCK_STREAM, 0); if( s < 0 ) return; tv.tv_sec = 0; tv.tv_usec = 500000; setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); if( connect(s, (struct sockaddr*)&dest, sizeof(struct sockaddr_in)) < 0 ) goto close; n = snprintf(buf, sizeof(buf), "GET /%s HTTP/1.0\r\n" "HOST: %s:%ld\r\n\r\n", path, addr, port); if( write(s, buf, n) < 1 ) goto close; while( (n = read(s, buf+nread, sizeof(buf)-nread-1)) > 0 ) { nread += n; buf[nread] = '\0'; n = nread; p = buf; while( !off && n-- > 0 ) { if(p[0]=='\r' && p[1]=='\n' && p[2]=='\r' && p[3]=='\n') { off = p + 4; do_headers = 1; } p++; } if( !off ) continue; if( do_headers ) { p = buf; if( strncmp(p, "HTTP/", 5) != 0 ) goto close; while(*p != ' ' && *p != '\t') p++; /* If we don't get a 200 status, ignore it */ if( strtol(p, NULL, 10) != 200 ) goto close; if( (p = strcasestr(p, "Content-Length:")) ) content_len = strtol(p+15, NULL, 10); do_headers = 0; } if( buf + nread - off >= content_len ) break; } close: close(s); if( !off ) return; nread -= off - buf; ParseNameValue(off, nread, &xml); model = GetValueFromNameValueList(&xml, "modelName"); serial = GetValueFromNameValueList(&xml, "serialNumber"); name = GetValueFromNameValueList(&xml, "friendlyName"); if( model ) { DPRINTF(E_DEBUG, L_SSDP, "Model: %s\n", model); if( strstr(model, "Roku SoundBridge") ) { type = ERokuSoundBridge; flags |= FLAG_MS_PFS; flags |= FLAG_AUDIO_ONLY; flags |= FLAG_MIME_WAV_WAV; } else if( strcmp(model, "Samsung DTV DMR") == 0 && serial ) { DPRINTF(E_DEBUG, L_SSDP, "Serial: %s\n", serial); /* The Series B I saw was 20081224DMR. Series A should be older than that. */ if( atoi(serial) > 20081200 ) { type = ESamsungSeriesB; flags |= FLAG_SAMSUNG; flags |= FLAG_DLNA; flags |= FLAG_NO_RESIZE; } } else { if( name && (strcmp(name, "marantz DMP") == 0) ) { type = EMarantzDMP; flags |= FLAG_DLNA; flags |= FLAG_MIME_WAV_WAV; } } } ClearNameValueList(&xml); if( !type ) return; /* Add this client to the cache if it's not there already. */ client = SearchClientCache(dest.sin_addr, 1); if( client < 0 ) { for( client=0; client<CLIENT_CACHE_SLOTS; client++ ) { if( clients[client].addr.s_addr ) continue; get_remote_mac(dest.sin_addr, clients[client].mac); clients[client].addr = dest.sin_addr; DPRINTF(E_DEBUG, L_SSDP, "Added client [%d/%s/%02X:%02X:%02X:%02X:%02X:%02X] to cache slot %d.\n", type, inet_ntoa(clients[client].addr), clients[client].mac[0], clients[client].mac[1], clients[client].mac[2], clients[client].mac[3], clients[client].mac[4], clients[client].mac[5], client); break; } } clients[client].type = type; clients[client].flags = flags; clients[client].age = time(NULL); }