--- /dev/null
+/*********************************************************************************\r
+ * PROJECT: MiMic\r
+ * --------------------------------------------------------------------------------\r
+ *\r
+ * This file is part of MiMic\r
+ * Copyright (C)2011 Ryo Iizuka\r
+ *\r
+ * MiMic is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU Lesser General Public License as published\r
+ * by the Free Software Foundation, either version 3 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public License\r
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
+ *\r
+ * For further information please contact.\r
+ * http://nyatla.jp/\r
+ * <airmail(at)ebony.plala.or.jp> or <nyatla(at)nyatla.jp>\r
+ *\r
+ *********************************************************************************/\r
+#include "NyLPC_cSsdpSocket.h"\r
+#include "NyLPC_uipService.h"\r
+#include "../uip/NyLPC_cUipService_protected.h"\r
+\r
+#include <stdio.h>\r
+#include <string.h>\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+#define PARSE_NULL 0\r
+#define PARSE_ST 0x01\r
+#define PARSE_MAN 0x11\r
+\r
+const static STR_UPNP_ROOT_DEVICE="upnp:rootdevice";\r
+\r
+struct TMSearchHeader\r
+{\r
+ struct NyLPC_THttpBasicHeader super;\r
+\r
+ const struct NyLPC_TUPnPDeviceRecord* _ref_devices;\r
+ NyLPC_TUInt16 _number_of_device;\r
+ /**\r
+ * パーサのステータス\r
+ */\r
+ NyLPC_TUInt8 st;\r
+ /**\r
+ * メモリ位置\r
+ */\r
+ const NyLPC_TChar* _rpos;\r
+ struct{\r
+ const NyLPC_TChar* st_str;\r
+ const NyLPC_TChar* man_str;\r
+ NyLPC_TUInt16 st_len;\r
+ NyLPC_TUInt16 man_len;\r
+ }result;\r
+};\r
+\r
+\r
+\r
+static NyLPC_TBool urlHandler(NyLPC_TcHttpBasicHeaderParser_t* i_inst,NyLPC_TChar i_c,struct NyLPC_THttpBasicHeader* o_out)\r
+{\r
+// *であるかを確認 未実装\r
+ return NyLPC_TBool_TRUE;\r
+}\r
+static NyLPC_TInt16 printIpAddr(const struct NyLPC_TIPv4Addr* i_ip,NyLPC_TChar* i_buf)\r
+{\r
+ NyLPC_TUInt32 ip;\r
+ NyLPC_TUInt8 v;\r
+ NyLPC_TInt8 l;\r
+ NyLPC_TChar* p=i_buf;\r
+ //IPをホストオーダーにする。\r
+ ip=NyLPC_NTOHL(i_ip->v);\r
+ for(l=3;l>=0;l--){\r
+ v=(ip>>(8*l))&0xff;\r
+ if(v<10){\r
+ //1桁\r
+ *(p+0)=v+'0';\r
+ *(p+1)='.';\r
+ p+=2;\r
+ }else if(v<100){\r
+ //2桁\r
+ *(p+0)=(v/10)+'0';\r
+ *(p+1)=(v%10)+'0';\r
+ *(p+2)='.';\r
+ p+=3;\r
+ }else{\r
+ //3桁\r
+ *(p+0)=(v/100)+'0';\r
+ *(p+1)=((v/10)%10)+'0';\r
+ *(p+2)=(v%10)+'0';\r
+ *(p+3)='.';\r
+ p+=4;\r
+ }\r
+ }\r
+ return p-i_buf-1;\r
+}\r
+#define TIMEOUT_IN_MS 100\r
+\r
+/**\r
+ * SERVER MessageHeaderの値\r
+ * 40文字以内であること。\r
+ */\r
+#define SERVER_MESSAGE_HEADER "MiMic/1.4;UPnP/1.0;MiMicUPnP/0.1"\r
+\r
+\r
+/**\r
+ * MsearchResponseを格納したTxパケットをAllocする。\r
+ * @param i_st\r
+ * ST値\r
+ * @param i_udn\r
+ * DDESCのUDNの値\r
+ * @param i_usn\r
+ * USNのサフィックスパラメータ\r
+ * @return\r
+ * MsearchResponseを格納したTXメモリ\r
+ */\r
+static void* allocMsearchResponeTx(\r
+ NyLPC_TcSsdpSocket_t* i_inst,\r
+ const NyLPC_TChar* i_st,\r
+ const NyLPC_TChar* i_udn,\r
+ const NyLPC_TChar* i_usn,\r
+ NyLPC_TUInt16 i_st_len,\r
+ NyLPC_TInt16* o_len)\r
+{\r
+ NyLPC_TChar* obuf;\r
+ NyLPC_TUInt16 l;\r
+ NyLPC_TUInt16 len_usn=(NyLPC_TUInt16)strlen(i_usn);\r
+ NyLPC_TUInt16 len_udn=(NyLPC_TUInt16)(i_udn!=NULL?strlen(i_udn):0);\r
+ NyLPC_TUInt16 len_location=(NyLPC_TUInt16)strlen(i_inst->location_path);\r
+\r
+ // //161Byte\r
+ // "HTTP/1.1 200 OK\r\n" //15+2=17\r
+ // "CACHE-CONTROL: max-age = 1800\r\n" //29+2=31\r
+ // "SERVER: [:40byte:]\r\n" //8+40+2=50\r
+ // "EXT: \r\n" //5+2 = 7\r
+ // "LOCATION: http://xxx.xxx.xxx.xxx:nnnnn/%s\r\n" //39+2=41\r
+ // "USN: %s%s\r\n" //5+2=7\r
+ // "ST: %s\r\n\r\n" //4+4=8\r
+ l=161+len_location+len_usn+len_udn+i_st_len;\r
+ obuf=NyLPC_cUdpSocket_allocSendBuf(&(i_inst->super),l,&l,TIMEOUT_IN_MS);\r
+ if(obuf==NULL){\r
+ return NULL;\r
+ }\r
+ //必要なメモリサイズを確保できた?\r
+ if(l<161+len_location+len_usn+len_udn+i_st_len)\r
+ {\r
+ NyLPC_cUdpSocket_releaseSendBuf(&i_inst->super,obuf);\r
+ return NULL;\r
+ }\r
+ //ワーク変数lの再初期化\r
+ l=0;\r
+ strcpy(obuf,\r
+ "HTTP/1.1 200 OK\r\n"\r
+ "CACHE-CONTROL: max-age = 120\r\n"\r
+ "SERVER: "SERVER_MESSAGE_HEADER"\r\n"\r
+ "EXT: \r\n"\r
+ "LOCATION: http://");\r
+ l+=strlen(obuf);\r
+ //IP addr:port\r\n\r
+ l+=printIpAddr(NyLPC_cUdpSocket_getSockIP(&i_inst->super),obuf+l);\r
+ *(obuf+l)=':';\r
+ l+=1+NyLPC_itoa(i_inst->location_port,obuf+l+1,10);\r
+ strcpy(obuf+l,i_inst->location_path);l+=len_location;\r
+ *(obuf+l+0)='\r';\r
+ *(obuf+l+1)='\n';\r
+ l+=2;\r
+ //USN: uuid:xxx\r
+ strcpy(obuf+l,"USN: "); l+=5;\r
+ strcpy(obuf+l,i_udn); l+=len_udn; //uuid:xxx\r
+ if(i_usn!=NULL){\r
+ *(obuf+l+0)=':';\r
+ *(obuf+l+1)=':';\r
+ l+=2;\r
+ strcpy(obuf+l,i_usn);l+=len_usn; //usn:xxx\r
+ }\r
+ *(obuf+l+0)='\r';\r
+ *(obuf+l+1)='\n';\r
+ l+=2;\r
+ //ST\r
+ strcpy(obuf+l,"ST: "); l+=3;\r
+ strcpy(obuf+l,i_st);l+=i_st_len;\r
+ strcpy(obuf+l,"\r\n\r\n"); l+=4;\r
+ *o_len=l;\r
+ return obuf;\r
+}\r
+\r
+\r
+/**\r
+ * MsearchResponseを格納したTxパケットをAllocする。\r
+ * @param i_udn\r
+ * udn\r
+ * @param i_udn\r
+ * DDESCのUDNの値\r
+ * @param i_usn\r
+ * USNのサフィックスパラメータ\r
+ * @return\r
+ * MsearchResponseを格納したTXメモリ\r
+ */\r
+static void* allocNotifyTx(\r
+ NyLPC_TcSsdpSocket_t* i_inst,\r
+ const NyLPC_TChar* i_udn,\r
+ const NyLPC_TChar* i_usn,\r
+ NyLPC_TInt16* o_len)\r
+{\r
+ NyLPC_TChar* obuf;\r
+ NyLPC_TUInt16 l,l2;\r
+ NyLPC_TUInt16 len_usn=(NyLPC_TUInt16)(i_usn!=NULL?strlen(i_usn):0);\r
+ NyLPC_TUInt16 len_udn=(NyLPC_TUInt16)strlen(i_udn);\r
+ NyLPC_TUInt16 len_location=(NyLPC_TUInt16)strlen(i_inst->location_path);\r
+\r
+ // //193Byte\r
+ // "NOTIFY * HTTP/1.1\r\n" //15+2=17\r
+ // "HOST: 239.255.255.250:1900\r\n" //26+2=28\r
+ // "CACHE-CONTROL: max-age = 1800\r\n" //29+2=31\r
+ // "SERVER: [:40byte:]\r\n" //8+40+2=50\r
+ // "NTS:alive\r\n" //9+2 =11\r
+ // "LOCATION: http://xxx.xxx.xxx.xxx:nnnnn/%s\r\n" //39+2=41\r
+ // "USN: %s%s\r\n" //5+2=7\r
+ // "NT: %s\r\n\r\n" //4+4=8\r
+ l2=193+len_location+len_usn+len_udn+(len_usn>0?len_usn:len_udn);\r
+ obuf=NyLPC_cUdpSocket_allocSendBuf(&(i_inst->super),l2,&l,TIMEOUT_IN_MS);\r
+ if(obuf==NULL){\r
+ return NULL;\r
+ }\r
+ //必要なメモリサイズを確保できた?\r
+ if(l<l2)\r
+ {\r
+ NyLPC_cUdpSocket_releaseSendBuf(&i_inst->super,obuf);\r
+ return NULL;\r
+ }\r
+ //ワーク変数lの再初期化\r
+ l=0;\r
+ strcpy(obuf,\r
+ "NOTIFY * HTTP/1.1\r\n"\r
+ "HOST: 239.255.255.250:1900\r\n"\r
+ "CACHE-CONTROL: max-age = 1800\r\n"\r
+ "SERVER: "SERVER_MESSAGE_HEADER"\r\n"\r
+ "NTS:alive\r\n"\r
+ "LOCATION: http://");\r
+ l+=strlen(obuf);\r
+ //IP addr:port\r\n\r
+ l+=printIpAddr(NyLPC_cUdpSocket_getSockIP(&i_inst->super),obuf+l);\r
+ *(obuf+l)=':';\r
+ l+=1+NyLPC_itoa(i_inst->location_port,obuf+l+1,10);\r
+ strcpy(obuf+l,i_inst->location_path);l+=len_location;\r
+ *(obuf+l+0)='\r';\r
+ *(obuf+l+1)='\n';\r
+ l+=2;\r
+ //USN: uuid:xxx\r
+ strcpy(obuf+l,"USN: "); l+=5;\r
+ strcpy(obuf+l,i_udn); l+=len_udn; //uuid:xxx\r
+ if(i_usn!=NULL){\r
+ *(obuf+l+0)=':';\r
+ *(obuf+l+1)=':';\r
+ l+=2;\r
+ strcpy(obuf+l,i_usn);l+=len_usn; //usn:xxx\r
+ }\r
+ *(obuf+l+0)='\r';\r
+ *(obuf+l+1)='\n';\r
+ l+=2;\r
+ //NT\r
+ strcpy(obuf+l,"NT: "); l+=4;\r
+ if(len_usn>0){\r
+ strcpy(obuf+l,i_usn);l+=len_usn;\r
+ }else{\r
+ strcpy(obuf+l,i_udn);l+=len_udn;\r
+ }\r
+ strcpy(obuf+l,"\r\n\r\n"); l+=4;\r
+ *o_len=l;\r
+ return obuf;\r
+}\r
+\r
+\r
+\r
+\r
+\r
+\r
+static NyLPC_TBool messageHandler(NyLPC_TcHttpBasicHeaderParser_t* i_inst,const NyLPC_TChar* i_name,NyLPC_TChar i_c,struct NyLPC_THttpBasicHeader* o_out)\r
+{\r
+ struct TMSearchHeader* header=(struct TMSearchHeader*)o_out;\r
+ switch(header->st)\r
+ {\r
+ case PARSE_NULL:\r
+ if(NyLPC_stricmp(i_name,"ST")==0){\r
+ //mode==ST\r
+ header->st=PARSE_ST;\r
+ header->result.st_str=header->_rpos;\r
+ }else if(NyLPC_stricmp(i_name,"MAN")==0){\r
+ //mode=MAN\r
+ header->st=PARSE_MAN;\r
+ header->result.man_str=header->_rpos;\r
+ }else{\r
+ //無視\r
+ }\r
+ break;\r
+ case PARSE_ST:\r
+ if(i_c=='\0')\r
+ {\r
+ header->result.st_len=header->_rpos-header->result.st_str;\r
+ header->st=PARSE_NULL;\r
+ }\r
+ break;\r
+ case PARSE_MAN:\r
+ if(i_c=='\0'){\r
+ header->result.man_len=header->_rpos-header->result.man_str;\r
+ header->st=PARSE_NULL;\r
+ }\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+ return NyLPC_TBool_TRUE;\r
+}\r
+\r
+/**\r
+ * デフォルトハンドラ\r
+ */\r
+static const struct NyLPC_TcHttpBasicHeaderParser_Handler handler=\r
+{\r
+ messageHandler,\r
+ urlHandler\r
+};\r
+\r
+static NyLPC_TBool parseHeader(struct TMSearchHeader* i_out,const void* i_rx,NyLPC_TInt16 i_rx_size)\r
+{\r
+ NyLPC_TInt16 i;\r
+ NyLPC_TcHttpBasicHeaderParser_t parser;\r
+ //headerの初期化\r
+ i_out->result.st_str=NULL;\r
+ i_out->result.man_str=NULL;\r
+ NyLPC_cHttpBasicHeaderParser_initialize(&parser,&handler);\r
+ NyLPC_cHttpBasicHeaderParser_parseInit(&parser,&(i_out->super));\r
+ for(i=0;i<i_rx_size;i++){\r
+ if(NyLPC_cHttpBasicHeaderParser_parseChar(&parser,((const char*)(i_rx)+i),1,&(i_out->super))<0){\r
+ NyLPC_cHttpBasicHeaderParser_finalize(&parser);\r
+ return NyLPC_TBool_FALSE;//ERROR\r
+ }\r
+ }\r
+ NyLPC_cHttpBasicHeaderParser_parseFinish(&parser,&(i_out->super));\r
+ NyLPC_cHttpBasicHeaderParser_finalize(&parser);\r
+ return NyLPC_TBool_TRUE;//OK\r
+}\r
+\r
+static NyLPC_TBool onPacket(NyLPC_TcUdpSocket_t* i_inst,const void* i_buf,const struct NyLPC_TIPv4RxInfo* i_info)\r
+{\r
+ //パケット解析\r
+ void* tx;\r
+ NyLPC_TInt16 tx_len;\r
+ NyLPC_TInt8 i,i2;\r
+ struct TMSearchHeader header;\r
+ NyLPC_TcSsdpSocket_t* sock=(NyLPC_TcSsdpSocket_t*)i_inst;\r
+ if(!parseHeader(&header,i_buf,i_info->size)){\r
+ NyLPC_OnErrorGoto(ERROR1);\r
+ }\r
+ //resultチェック\r
+ if(header.result.man_str!=NULL || header.result.st_str!=NULL){\r
+ NyLPC_OnErrorGoto(ERROR1);\r
+ }\r
+ //MANチェック\r
+ if(strncmp("ssdp:discover",header.result.man_str,header.result.man_len)!=0){\r
+ NyLPC_OnErrorGoto(ERROR1);\r
+ }\r
+ //STによる処理分岐\r
+ if(strncmp("ssdp:all",header.result.st_str,8)==0){\r
+ //全デバイスの送信\r
+ }else if(strncmp("uuid:",header.result.st_str,5)==0){\r
+ //UDNの一致するデバイスの送信\r
+ NyLPC_TInt16 i;\r
+ for(i=sock->number_of_device_record-1;i>=0;i--){\r
+ if(strncmp(header.result.st_str,sock->ref_device_record[i].udn,header.result.st_len)){\r
+ //UDN一致\r
+ tx=allocMsearchResponeTx(\r
+ sock,header.result.st_str,\r
+ sock->ref_device_record[i].udn,NULL,\r
+ header.result.st_len,\r
+ &tx_len);\r
+ if(tx==NULL){\r
+ NyLPC_OnErrorGoto(ERROR1);\r
+ }\r
+ if(!NyLPC_cUdpSocket_psend(i_inst,&i_info->peer_ip,i_info->peer_port,tx,tx_len)){\r
+ NyLPC_OnErrorGoto(ERROR2);\r
+ }\r
+ break;//送信処理終了\r
+ }\r
+ }\r
+ }else if(strncmp(STR_UPNP_ROOT_DEVICE,header.result.st_str,15)==0){\r
+ //rootDeviceはdevice0\r
+ tx=allocMsearchResponeTx(\r
+ sock,header.result.st_str,\r
+ sock->ref_device_record[0].udn,sock->ref_device_record[0].device_type,\r
+ header.result.st_len,\r
+ &tx_len);\r
+ if(tx==NULL){\r
+ NyLPC_OnErrorGoto(ERROR1);\r
+ }\r
+ if(!NyLPC_cUdpSocket_psend(i_inst,&i_info->peer_ip,i_info->peer_port,tx,tx_len)){\r
+ NyLPC_OnErrorGoto(ERROR2);\r
+ }\r
+ }else if(strncmp("urn:",header.result.st_str,4)==0){\r
+ for(i=0;i<header._number_of_device;i++){\r
+ //urn一致チェック\r
+ if(strncmp(sock->ref_device_record[i].device_type,header.result.st_str,header.result.st_len)==0){\r
+ //deviceに一致\r
+ tx=allocMsearchResponeTx(\r
+ sock,header.result.st_str,\r
+ sock->ref_device_record[i].udn,sock->ref_device_record[i].device_type,\r
+ header.result.st_len,\r
+ &tx_len);\r
+ if(tx==NULL){\r
+ NyLPC_OnErrorGoto(ERROR1);\r
+ }\r
+ if(!NyLPC_cUdpSocket_psend(i_inst,&i_info->peer_ip,i_info->peer_port,tx,tx_len)){\r
+ NyLPC_OnErrorGoto(ERROR2);\r
+ }\r
+ continue;\r
+ }\r
+ for(i2=0;i2<sock->ref_device_record[i].number_of_service;i2++){\r
+ if(strncmp(sock->ref_device_record[i].services[i2].service_type,header.result.st_str,header.result.st_len)==0){\r
+ //serviceに一致\r
+ tx=allocMsearchResponeTx(\r
+ sock,header.result.st_str,\r
+ sock->ref_device_record[i].udn,sock->ref_device_record[i].services[i2].service_type,\r
+ header.result.st_len,\r
+ &tx_len);\r
+ if(tx==NULL){\r
+ NyLPC_OnErrorGoto(ERROR1);\r
+ }\r
+ if(!NyLPC_cUdpSocket_psend(i_inst,&i_info->peer_ip,i_info->peer_port,tx,tx_len)){\r
+ NyLPC_OnErrorGoto(ERROR2);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ //正常終了\r
+ return NyLPC_TBool_FALSE;\r
+ERROR2:\r
+ NyLPC_cUdpSocket_releaseSendBuf(i_inst,tx);\r
+ERROR1:\r
+ return NyLPC_TBool_FALSE;\r
+}\r
+static const struct NyLPC_TIPv4Addr SSDP_MCAST_IPADDR=NyLPC_TIPv4Addr_pack(239,225,225,250);\r
+\r
+\r
+void NyLPC_cSsdpSocket_initialize(\r
+ NyLPC_TcSsdpSocket_t* i_inst,\r
+ const struct NyLPC_TUPnPDeviceRecord* i_ref_dev_record,NyLPC_TUInt8 i_number_of_devices,\r
+ NyLPC_TUInt16 i_server_port,const NyLPC_TChar* i_ref_location_path)\r
+{\r
+ NyLPC_cUdpSocket_initialize(&(i_inst->super),1900,NULL,0);\r
+ NyLPC_cUdpSocket_joinMulticast(&(i_inst->super),&SSDP_MCAST_IPADDR);\r
+\r
+ i_inst->ref_device_record=i_ref_dev_record;\r
+ i_inst->number_of_device_record=i_number_of_devices;\r
+ i_inst->location_port=i_server_port;\r
+ i_inst->location_path=i_ref_location_path;\r
+}\r
+void NyLPC_cSsdpSocket_finalize(NyLPC_TcSsdpSocket_t* i_inst)\r
+{\r
+ NyLPC_cUdpSocket_finalize(&(i_inst->super));\r
+}\r
+\r
+void NyLPC_cSsdpSocket_notify(NyLPC_TcSsdpSocket_t* i_inst)\r
+{\r
+ void* tx;\r
+ NyLPC_TInt16 tx_len;\r
+ NyLPC_TUInt8 i,i2;\r
+ //rootdevice\r
+ tx=allocNotifyTx(\r
+ i_inst,\r
+ i_inst->ref_device_record[0].udn,STR_UPNP_ROOT_DEVICE,\r
+ &tx_len);\r
+ if(tx==NULL){\r
+ NyLPC_OnErrorGoto(ERROR1);\r
+ }\r
+ if(!NyLPC_cUdpSocket_psend(&i_inst->super,&SSDP_MCAST_IPADDR,1900,tx,tx_len)){\r
+ NyLPC_OnErrorGoto(ERROR2);\r
+ }\r
+ //all device\r
+ for(i=0;i<i_inst->number_of_device_record;i++){\r
+ //uuid\r
+ tx=allocNotifyTx(\r
+ i_inst,\r
+ i_inst->ref_device_record[i].udn,NULL,\r
+ &tx_len);\r
+ if(tx==NULL){\r
+ NyLPC_OnErrorGoto(ERROR1);\r
+ }\r
+ if(!NyLPC_cUdpSocket_psend(&i_inst->super,&SSDP_MCAST_IPADDR,1900,tx,tx_len)){\r
+ NyLPC_OnErrorGoto(ERROR2);\r
+ }\r
+ //devicatype\r
+ tx=allocNotifyTx(\r
+ i_inst,\r
+ i_inst->ref_device_record[i].udn,i_inst->ref_device_record[i].device_type,\r
+ &tx_len);\r
+ if(tx==NULL){\r
+ NyLPC_OnErrorGoto(ERROR1);\r
+ }\r
+ if(!NyLPC_cUdpSocket_psend(&i_inst->super,&SSDP_MCAST_IPADDR,1900,tx,tx_len)){\r
+ NyLPC_OnErrorGoto(ERROR2);\r
+ }\r
+ for(i2=0;i2<i_inst->ref_device_record[i].number_of_service;i2++){\r
+ tx=allocNotifyTx(\r
+ i_inst,\r
+ i_inst->ref_device_record[i].udn,i_inst->ref_device_record[i].services[i2].service_type,\r
+ &tx_len);\r
+ if(tx==NULL){\r
+ NyLPC_OnErrorGoto(ERROR1);\r
+ }\r
+ if(!NyLPC_cUdpSocket_psend(&i_inst->super,&SSDP_MCAST_IPADDR,1900,tx,tx_len)){\r
+ NyLPC_OnErrorGoto(ERROR2);\r
+ }\r
+ }\r
+ }\r
+ return;\r
+ERROR2:\r
+ NyLPC_cUdpSocket_releaseSendBuf(&i_inst->super,tx);\r
+ERROR1:\r
+ return;\r
+}\r
+\r