bool DEWFcK::wfConnect(SECURITY security, const char * szSsid, const byte * rgbKey, int iKey, IPSTATUS * pStatus) { IPSTATUS status = ipsFailed; deIPInit(); if(isWFInitialized(&status)) { switch(_wfState) { case idle: case scanReady: case keygenReady: case connecting: if(_pNwWF->Connect(security, (const uint8_t *) szSsid, rgbKey, iKey, true, &status)) { _wfState = connected; } else if(IsIPStatusAnError(status)) { _wfState = idle; } break; default: status = ipsInvalidOperation; break; } } AssignStatusSafely(pStatus, status); return(_wfState == connected); }
// this will free the socket if it can't open it TCPSOCKET * TCPInitSocket(const LLADP * pLLAdp, TCPSOCKET * pSocketOpen, HPMGR hPMGR, const void * pIPvXDest, uint16_t portRemote, uint16_t portLocal, IPSTATUS * pStatus) { IPSTATUS status = ipsSuccess; SMGR * pSMGR = NULL; uint32_t cb = 0; uint32_t cPages = 0; if(pSocketOpen == NULL) { AssignStatusSafely(pStatus, ipsSocketNULL); return(NULL); } else if(hPMGR == NULL) { AssignStatusSafely(pStatus, ipsNoPMGRGiven); } // find a port if(portLocal == portDynamicallyAssign) { portLocal = GetEphemeralPort(&g_ffptActiveTCPSockets, &g_nextTCPEphemeralPort); } // get the sequence number // we have to get this BEFORE we memset the socket // as the timewait may be the socket we are clearing. // also, if we are listening, we can only assign the sequence number // when we now our remote port and IP as to not duplicate and to watch seq numbers. if(portRemote != portListen) { SKTPORTPAIR portPair; // remote, local portPair.portRemote = portRemote; portPair.portLocal = portLocal; // worry about the 2MSL timewait issue, get an appropriate sequence number if(TCPIsInUse(pLLAdp, portPair.portPair, pIPvXDest)) { AssignStatusSafely(pStatus, ipsPortPairAndIPAlreadyActive); return(NULL); } } // build the stream manager to point to the page handler // first build the stream to the RxStream and TxStream // Rx is the 1st pSocketOpen->hPMGR = hPMGR; // calculate the size of the 2 embedded streams and how to partition the socket stream, stream // the Rx is the first indirect stream follwed by the Tx indirect stream in the socket stream cb = (1 << ((PMGR *) hPMGR)->pf2PerPage); cPages = ((PMGR *) hPMGR)->cPages; pSocketOpen->cbRxSMGR = GetSMGRSize(min(((cTCPRXPages * cb) - sizeof(SMGR)), cPages)); pSocketOpen->cbTxSMGR = GetSMGRSize(min(((cTCPTXPages * cb) - sizeof(SMGR)), cPages)); cb = pSocketOpen->cbRxSMGR + pSocketOpen->cbTxSMGR; if(SMGRInit(&pSocketOpen->smgrRxTxBuff, cbMAXTCPSreamRecord, hPMGR) != (HSMGR) &pSocketOpen->smgrRxTxBuff) { status = ispOutOfMemory; } else if((pSMGR = (SMGR*)alloca(cb)) == NULL) { status = ispOutOfMemory; } else if(SMGRInit(pSMGR, pSocketOpen->cbRxSMGR, hPMGR) == NULL || SMGRInit((SMGR *) (((uint8_t *) pSMGR) + pSocketOpen->cbRxSMGR), pSocketOpen->cbTxSMGR, hPMGR) == NULL) { status = ispOutOfMemory; } else if(SMGRWrite(&pSocketOpen->smgrRxTxBuff, 0, pSMGR, cb) != cb) { status = ispOutOfMemory; } AssignStatusSafely(pStatus, status); if(IsIPStatusAnError(status)) { SMGRFree((HSMGR) pSMGR); SMGRFree((HSMGR) (((uint8_t *) pSMGR) + pSocketOpen->cbRxSMGR)); SMGRFree((HSMGR) &pSocketOpen->smgrRxTxBuff); IPSReleaseSocket(pSocketOpen); return(NULL); } // make the socket pSocketOpen->tcpState = tcpAllocated; pSocketOpen->s.portLocal = portLocal; pSocketOpen->s.portRemote = portRemote; pSocketOpen->s.pLLAdp = pLLAdp; memcpy(&pSocketOpen->s.ipRemote, pIPvXDest, ILIPSize(pLLAdp)); // get a new seq nbr pSocketOpen->sndISS = TCPGetSeqNumber(pLLAdp); // max I will allow to come in. RFC 1122 4.2.2.6 pSocketOpen->cbLocalMSS = min((PMGRMaxAlloc(hPMGR) >> 2), (uint16_t) (LLGetMTUR(pLLAdp) - 20)); // Jacobson rule pSocketOpen->RTTsa = RTTsaINIT; pSocketOpen->RTTsv = RTTsvINIT; pSocketOpen->tRTO_SET = RTO(pSocketOpen); pSocketOpen->tRTOCur = pSocketOpen->tRTO_SET; return(pSocketOpen); }
/*** GCMD::ACTION ProcessClient(CLIENTINFO * pClientInfo) * * Parameters: * pClientInfo - a pointer to CLIENTINFO structure with the socket and local data used by the connection * * Return Values: * GCMD::ACTION - Generally GCMD::CONTINUE is returned to keep the server running * However if GCMD::RESTART is returned, the server is restarted * And if GCMD::TERMINATE is returned, the server is cleanly shutdown and loops forever doing nothing. * * Description: * * In the CLINTINFO structure there is an fInUse flag, and this is what tells the server * that the CLINTINFO has an active connection. Only active connections are processed by the server * so this function is only called with pClientInfo->fInUse == true. This function sets it false when the connection * is closed or lost. * * Having said that, there are a few cases when the connection will be dropped. For example if the other side * unexpectedly drops the connection, or the network goes down. * * When the connection is closed (state == STOPCLIENT) for whatever reason, the HTML rendering function will be called with * pClientInfo->htmlState == HTTPDISCONNECT. This is the only intential time the HTML Rendering function will * be called with the connection closed. This is to allow the Rendering page to deinitialize any state. * So if a file is open, or hardware is process, the rendering page function will have an opertunity to close * resources. This is particularly useful if the connection is unexpectedly dropped, this will allow * for cleanup. When the HTML rendering function is called with HTTPDISCONNECT, GCMD::DONE is expected to be returned. * * ------------------------------------------------------------ */ GCMD::ACTION ProcessClient(CLIENTINFO * pClientInfo) { GCMD::ACTION action = GCMD::CONTINUE; IPSTATUS status = ipsSuccess; uint32_t tCur = millis(); // timeout occured, get out if(pClientInfo->clientState != START && (tCur - pClientInfo->tStartClient) >= (secClientTO * 1000)) { Serial.print("Timeout on client: 0x"); Serial.println((uint32_t) pClientInfo, HEX); pClientInfo->nextClientState = EXIT; // if no data came in at all, then just close the connection if(pClientInfo->cbRead == 0) { pClientInfo->clientState = STOPCLIENT; } // if some data came in, then give the data to the default HTML page. else if(pClientInfo->ComposeHTMLPage == NULL) { pClientInfo->clientState = DEFAULTCMD; } else { pClientInfo->clientState = PROCESSHTML; pClientInfo->htmlState = HTTPTIMEOUT; } } // lost the connection, get out if(((CLIENTSTATE) pClientInfo->clientState < STOPCLIENT) && !pClientInfo->pTCPClient->isEstablished()) { Serial.println("Connection Lost"); pClientInfo->clientState = STOPCLIENT; pClientInfo->tStartClient = tCur; pClientInfo->nextClientState = EXIT; } switch((CLIENTSTATE) pClientInfo->clientState) { case START: Serial.println("New Client detected"); // we know for the most part, the CLIENTINFO is zero'ed // so only set non-zero values pClientInfo->pbOut = pClientInfo->rgbOut; pClientInfo->htmlState = HTTPSTART; // forces the initialization state to be executed pClientInfo->tStartClient = tCur; pClientInfo->clientState = READINPUT; pClientInfo->nextClientState = WAITCMD; break; case READINPUT: // read in the HTTP URL, any additional data. if(pClientInfo->pTCPClient->available() >= 0 && pClientInfo->cbRead < sizeof(pClientInfo->rgbIn)) { pClientInfo->cbRead += pClientInfo->pTCPClient->readStream(&pClientInfo->rgbIn[pClientInfo->cbRead], sizeof(pClientInfo->rgbIn) - pClientInfo->cbRead); } pClientInfo->clientState = pClientInfo->nextClientState; break; case WAITCMD: // until we read something, match, or timeout, we keep reading and waiting. pClientInfo->clientState = READINPUT; pClientInfo->nextClientState = WAITCMD; // check the match strings, only if we have something to compare against if(pClientInfo->cbRead > 0) { bool fPartialMatch = false; // now look to see if HTML redering pages matches this URL for(uint32_t i = 0; i < CNTHTTPCMD; i++) { // does this entry have anything in it? if(rgHttpCmd[i].cbMatch > 0) { uint32_t cbCmp = rgHttpCmd[i].cbMatch > pClientInfo->cbRead ? pClientInfo->cbRead : rgHttpCmd[i].cbMatch; // see if we found a matching URL if(memcmp(pClientInfo->rgbIn, rgHttpCmd[i].szMatchString, cbCmp) == 0) { // we either have a partial or full match fPartialMatch = true; // we have a full match; we know what dynamic page to call if(cbCmp == rgHttpCmd[i].cbMatch) { pClientInfo->ComposeHTMLPage = rgHttpCmd[i].ComposeHTMLPage; pClientInfo->clientState = DISPLAYTIME; pClientInfo->tStartClient = tCur; // fournd it, get out. break; } } } } // if we didn't find a perfect match, then lets see if we are done anyway if(pClientInfo->clientState == READINPUT) { // only quit if we can't read anymore; our input buffer is full // otherwise keep reading, we may get more bytes to give us a full match if(!fPartialMatch || pClientInfo->cbRead == sizeof(pClientInfo->rgbIn) || (pClientInfo->cbRead >= 4 && pClientInfo->rgbIn[pClientInfo->cbRead-4] == '\r' && pClientInfo->rgbIn[pClientInfo->cbRead-3] == '\n' && pClientInfo->rgbIn[pClientInfo->cbRead-2] == '\r' && pClientInfo->rgbIn[pClientInfo->cbRead-1] == '\n' ) ) { pClientInfo->clientState = DEFAULTCMD; pClientInfo->tStartClient = tCur; } } } break; case DEFAULTCMD: Serial.println("Default page called"); pClientInfo->ComposeHTMLPage = DefaultHTMLPage; pClientInfo->tStartClient = tCur; pClientInfo->clientState = DISPLAYTIME; break; case DISPLAYTIME: { #ifndef NOTIME unsigned int epochTime = 0; char szTemp[256]; epochTime = deIPcK.secondsSinceEpoch(); GetDayAndTime(epochTime, szTemp); Serial.println(szTemp); #endif pClientInfo->clientState = PROCESSHTML; pClientInfo->tStartClient = tCur; } break; case PROCESSHTML: // process the HTML page switch((action = pClientInfo->ComposeHTMLPage(pClientInfo))) { // just come back to this state case GCMD::CONTINUE: pClientInfo->clientState = PROCESSHTML; break; // TODO, read more from the input. case GCMD::READ: pClientInfo->nextClientState = PROCESSHTML; pClientInfo->clientState = READINPUT; break; // we have data to write out case GCMD::WRITE: pClientInfo->nextClientState = PROCESSHTML; pClientInfo->clientState = WRITEBUFFER; break; // Done processing the client, close the client and get out case GCMD::DONE: pClientInfo->nextClientState = EXIT; // we are done! pClientInfo->clientState = STOPCLIENT; break; case GCMD::RESTART: pClientInfo->nextClientState = RESTART; // we want to terminate the server pClientInfo->clientState = STOPCLIENT; break; case GCMD::TERMINATE: pClientInfo->nextClientState = TERMINATE; // we want to terminate the server pClientInfo->clientState = STOPCLIENT; break; case GCMD::REBOOT: pClientInfo->nextClientState = REBOOT; // we want to reboot the server pClientInfo->clientState = STOPCLIENT; break; case GCMD::GETLINE: pClientInfo->clientState = PARSENEXTLINE; break; default: Serial.print("Unsupported compose command detected: "); Serial.println(action, DEC); pClientInfo->nextClientState = EXIT; // we are done! pClientInfo->clientState = STOPCLIENT; break; } pClientInfo->cbWritten = 0; pClientInfo->tStartClient = tCur; break; case PARSENEXTLINE: { uint32_t i = 0; boolean fFoundNewLine = false; boolean fNullTerminator = false; // we are looking for either a /r/n or a /0. // if we find the /r/n first, than replace the /r/n with /0 and that is our first line // otherwise if we find the /0, go find the next /r/n for(i = 0; i < pClientInfo->cbRead; i++) { if( (fFoundNewLine = pClientInfo->rgbIn[i] == '\n') || (fNullTerminator = pClientInfo->rgbIn[i] == '\0') ) { break; } } // found the line end, terminate it and return. if(fFoundNewLine) { pClientInfo->rgbIn[i] = '\0'; if(i > 0 && pClientInfo->rgbIn[i-1] == '\r') { pClientInfo->rgbIn[i-1] = '\0'; } pClientInfo->clientState = PROCESSHTML; } else if(fNullTerminator) { // go to beyond the \0 for( ; i < pClientInfo->cbRead && pClientInfo->rgbIn[i] == '\0'; i++); if(i == pClientInfo->cbRead) { // nothing left in the buffer to read, so we can reset it. pClientInfo->cbRead = 0; pClientInfo->clientState = READINPUT; pClientInfo->nextClientState = PARSENEXTLINE; if(pClientInfo->rgbOverflow[0] != '\0'); { pClientInfo->rgbIn[0] = pClientInfo->rgbOverflow[0]; pClientInfo->rgbOverflow[0] = '\0'; pClientInfo->cbRead = 1; } } // copy the end of the buffer to the front of the buffer // memcpy should be a safe overlapping copy. else { pClientInfo->cbRead -= i; memcpy(pClientInfo->rgbIn, &pClientInfo->rgbIn[i], pClientInfo->cbRead); // stay at this state to go find the end of line } } // this is a hard condition, our input buffer is full, and it doesn't even contain a full line. else if(i == sizeof(pClientInfo->rgbIn)) { // save away the last byte and return it as a line, even though there is a line break. pClientInfo->rgbOverflow[0] = pClientInfo->rgbIn[sizeof(pClientInfo->rgbIn)-1]; pClientInfo->rgbIn[sizeof(pClientInfo->rgbIn)-1] = '\0'; pClientInfo->clientState = PROCESSHTML; } // we need to read more data. else { pClientInfo->clientState = READINPUT; pClientInfo->nextClientState = PARSENEXTLINE; } } break; case WRITEBUFFER: // see if we are done if(pClientInfo->cbWritten == pClientInfo->cbWrite) { pClientInfo->clientState = pClientInfo->nextClientState; pClientInfo->nextClientState = STOPCLIENT; break; } // otherwise we have data to write pClientInfo->cbWritten += pClientInfo->pTCPClient->writeStream(&pClientInfo->pbOut[pClientInfo->cbWritten], pClientInfo->cbWrite - pClientInfo->cbWritten, &status); // got an error, terminate the connection if(IsIPStatusAnError(status)) { pClientInfo->clientState = STOPCLIENT; pClientInfo->nextClientState = EXIT; } // or we are done now after the write else if(pClientInfo->cbWritten == pClientInfo->cbWrite) { pClientInfo->clientState = pClientInfo->nextClientState; pClientInfo->nextClientState = STOPCLIENT; } pClientInfo->tStartClient = tCur; break; case STOPCLIENT: Serial.print("Closing connection for client: 0x"); Serial.println((uint32_t) pClientInfo, HEX); pClientInfo->pTCPClient->close(); if(pClientInfo->ComposeHTMLPage != NULL) { pClientInfo->htmlState = HTTPDISCONNECT; pClientInfo->ComposeHTMLPage(pClientInfo); } pClientInfo->clientState = pClientInfo->nextClientState; pClientInfo->tStartClient = tCur; return(GCMD::ADDSOCKET); break; case EXIT: break; case RESTART: Serial.println("Restart Commanded"); return(GCMD::RESTART); break; case TERMINATE: Serial.println("Termination Commanded"); return(GCMD::TERMINATE); break; case REBOOT: Serial.println("Reboot Commanded"); return(GCMD::REBOOT); break; default: Serial.print("Unknown client state: "); Serial.println(pClientInfo->clientState, DEC); pClientInfo->clientState = STOPCLIENT; pClientInfo->nextClientState = EXIT; pClientInfo->tStartClient = tCur; break; } return(GCMD::CONTINUE); }
/***************************************************************************** Function: bool TCPIsConnected(SOCKET * pSocket, IPSTATUS * pStatus) Description: This is sort of like TCPIsEstablished except it is for any connected state. Think of being connected as being in the process of getting or closing a connection and being established. Established is a fully connected duplex condtion where both sides are ready to send and receive data. Parameters: pSocket: The socket to see if it is in the established state pStatus: A pointer to a status variable to recieve the status of the connection This may be NULL if you don't care about the status Returns: true if the connection is in the process of connecting, half or full duplex connection, or in the process of closing but not yet closed. false if the conneciton is not in any kind of active condition. ***************************************************************************/ bool TCPIsConnected(HSOCKET hSocket, IPSTATUS * pStatus) { TCPSOCKET * pSocket = (TCPSOCKET *) hSocket; IPSTATUS status = ipsUnknowTCPState; // check to see if we are even connected if(pSocket == NULL) { AssignStatusSafely(pStatus, ipsSocketNULL); return(false); } // Otherwise, what is our state? switch(pSocket->tcpState) { case tcpListen: case tcpSynSent: case tcpSynReceivedWhileListening: case tcpSynReceived: case tcpEstablished: case tcpFinWait1: case tcpFinWait2: case tcpCloseWait: case tcpClosing: case tcpLastAck: status = IPStatusFromTCPState(pSocket->tcpState); break; case tcpWaitUserClose: if(TCPAvailable(pSocket, NULL) > 0) { status = IPStatusFromTCPState(pSocket->tcpState); } else { status = IPErrorFromTCPState(pSocket->tcpState); } break; case tcpUnassigned: case tcpAllocated: case tcpInvalid: case tcpClosed: status = IPErrorFromTCPState(pSocket->tcpState); break; default: status = ipsUnknowTCPState; break; } AssignStatusSafely(pStatus, status); if(IsIPStatusAnError(status)) { return(false); } else if(!ILIsIPNetworkReady(pSocket->s.pLLAdp, &status)) { AssignStatusSafely(pStatus, status); return(false); } else if(pSocket->tcpState == tcpListen) { return(false); } return(true); }
/***************************************************************************** Function: SOCKET * TCPOpen(const LLADP * pLLAdp, const SOCKETPOOL * pSocketPool, void * pIPvXDest, uint16_t portRemote, uint16_t portLocal, IPSTATUS * pStatus) Summary: Opens a Socket for both Client and Server. If portRemote == 0 The socket is opened for listening. Description: Precondition: Parameters: pLLAdp - The adaptor to use pSocket - A pointer to the socket to use hPMGR - A handle to the page manager to create the socket stream. pIPvXDest - The Dest IP to connect to if a client, ignored for a server open and may be NULL portRemote - The remote port to connect to if Client, MUST be 0 if this is a server open for listen portLocal - Local port to use, one will be assigned if zero pStatus - A pointer to a status variable to recieve the status of the open, This may be NULL Returns: The Socket if opened, NULL on failure ***************************************************************************/ HSOCKET TCPOpenWithSocket(const LLADP * pLLAdp, TCPSOCKET * pSocket, HPMGR hPMGR, const void * pIPvXDest, uint16_t portRemote, uint16_t portLocal, IPSTATUS * pStatus) { IPSTATUS status = ipsSuccess; IPSTACK * pIpStack = NULL; uint32_t cbOptions = 0; // make sure pIPvXDest points to something if(portRemote == 0 || pIPvXDest == NULL) { pIPvXDest = &IPv6NONE; } if(pSocket == NULL) { AssignStatusSafely(pStatus, ipsNoSocketsAvailable); return(NULL); } else if(pLLAdp == NULL) { status = ipsAdaptorMustBeSpecified; } // if this is a client open else if( (pSocket = TCPInitSocket(pLLAdp, pSocket, hPMGR, pIPvXDest, portRemote, portLocal, &status)) != NULL && portRemote != portListen && (pIpStack = TCPCreateSyn(pSocket, &cbOptions, &status)) != NULL) { pSocket->tcpState = tcpSynSent; pSocket->tLastAck = SYSGetMilliSecond(); // start the connection process TCPTransmit(pIpStack, pSocket, 1, cbOptions, false, SYSGetMilliSecond(), &status); } // else this is a server open / or an error getting the socket which we will abort below in the error check else if(pSocket != NULL) { pSocket->tcpState = tcpListen; } // we got an error somewhere; clean up if(IsIPStatusAnError(status)) { if(pSocket != NULL) { pSocket->tcpState = tcpUnassigned; TCPResetSocket(pSocket); } IPSRelease(pIpStack); pSocket = NULL; AssignStatusSafely(pStatus, status); return(NULL); } // put on the listening list. FFInPacket(&g_ffptActiveTCPSockets, pSocket); pSocket->fSocketOpen = true; AssignStatusSafely(pStatus, status); return((HSOCKET) pSocket); }
bool DNSInit(const LLADP * pLLAdp, void * rgbDNSMem, uint32_t cbDNSMem, HPMGR hPMGR, IPSTATUS * pStatus) { IPSTATUS status = ipsSuccess; DNSMEM * pDNSMem = (DNSMEM *) rgbDNSMem; uint32_t i = 0; if(pLLAdp == NULL) { status = ipsAdaptorMustBeSpecified; } else if(hPMGR == NULL) { status = ipsNoPMGRGiven; } else if(rgbDNSMem == NULL) { status = ipsDNSMemIsNULL; } else if(cbDNSMem < sizeof(DNSMEM)) { status = ipsDNSNotEnoughMem; } else if(pLLAdp->pDNSMem != NULL) { status = ipsDNSAlreadyInitialized; } else if(ILIsIPv6(pLLAdp)) { status = ipsIPv6NotSupported; } if(IsIPStatusAnError(status)) { AssignStatusSafely(pStatus, status); return(false); } memset(pDNSMem, 0, cbDNSMem); if(&pDNSMem->socket != UDPOpenWithSocket(pLLAdp, &pDNSMem->socket, hPMGR, &IPv4BROADCAST, portDNSServer, portDynamicallyAssign, &status) || IsIPStatusAnError(status)) { UDPClose(&pDNSMem->socket); AssignStatusSafely(pStatus, status); return(false); } pDNSMem->dnsNSMax = (cbDNSMem - sizeof(DNSMEM)) / sizeof(IPv4or6); pDNSMem->cDhcpNS = 0; pDNSMem->iDNSCur = DNSiInvalid; pDNSMem->iDNSWorks = DNSiInvalid; pDNSMem->dnsState = dnsReady; pDNSMem->cTry = 0; ((LLADP *) pLLAdp)->pDNSMem = pDNSMem; // initialize them all to the Google NS // that way there is something in all of the slots // this will get over written if DHCP is used. for(i=0; i<pDNSMem->dnsNSMax; i++) { if((i % 2) == 0) { pDNSMem->dnsNS[i].ipv4.u32 = 0x04040808; // Google public DNS server } else { pDNSMem->dnsNS[i].ipv4.u32 = 0x08080808; // Google public DNS server } } AssignStatusSafely(pStatus, status); return(true); }
// Notes on dnsNSMax and cDhcpNS. You would think that we should only cycle through cDhcpNS as this is the number // DNS servers given to us by DHCP, however, sometimes DHCP does not give us good DNS servers and for the // SNTP server to work, we need a good DNS server. So we continue to check the default, pre initialized google DNS // servers after the DHCP DNS servers are checked first. If we get a lot of DNS servers from DHCP, then ultimately // all of the pre installed google servers will be overwritten in the dnsNSMax list, but if we got a lot of DNS servers // the assumption is that they are good ones. In particular, I found that Verizon did not give me very good DNS servers, they // could not resolve the SNTP time servers. static void DNSStateMachine(const LLADP * pLLAdp) { IPSTATUS status = ipsSuccess; if(pLLAdp == NULL || pLLAdp->pDNSMem == NULL || !ILIsIPSetup(pLLAdp, NULL)) { return; } switch(pLLAdp->pDNSMem->dnsState) { case dnsSend: if(pLLAdp->pDNSMem->dnsNSMax == 0) { pLLAdp->pDNSMem->dnsState = dnsReady; pLLAdp->pDNSMem->cTry = 0; break; } else if(pLLAdp->pDNSMem->cTry >= (pLLAdp->pDNSMem->dnsNSMax * DNSMINTRY)) { if(pLLAdp->pDNSMem->iDNSCur == pLLAdp->pDNSMem->iDNSWorks) { pLLAdp->pDNSMem->iDNSWorks = DNSiInvalid; } pLLAdp->pDNSMem->iDNSCur = DNSiInvalid; pLLAdp->pDNSMem->dnsState = dnsReady; pLLAdp->pDNSMem->cTry = 0; break; } else if(pLLAdp->pDNSMem->iDNSWorks < pLLAdp->pDNSMem->dnsNSMax) { pLLAdp->pDNSMem->iDNSCur = pLLAdp->pDNSMem->iDNSWorks; } else if(pLLAdp->pDNSMem->iDNSCur >= pLLAdp->pDNSMem->dnsNSMax) { pLLAdp->pDNSMem->iDNSCur = 0; } else { pLLAdp->pDNSMem->iDNSCur = (pLLAdp->pDNSMem->iDNSCur + 1) % pLLAdp->pDNSMem->dnsNSMax; } // set this up for an attempt to get the IP address memcpy(&pLLAdp->pDNSMem->socket.s.ipRemote, &pLLAdp->pDNSMem->dnsNS[pLLAdp->pDNSMem->iDNSCur], ILIPSize(pLLAdp)); // fall thru on success case dnsReadySend: // clear the current IP address memset(&pLLAdp->pDNSMem->ip, 0, sizeof(IPv4or6)); // just make this unique for this pass pLLAdp->pDNSMem->dnsDG.dnsHdr.ID++; // set my timers and counts pLLAdp->pDNSMem->cTry++; pLLAdp->pDNSMem->tTimeout = dnsWaitForRetry; pLLAdp->pDNSMem->tStart = SYSGetMilliSecond(); // send out the DNS datagram UDPSend(&pLLAdp->pDNSMem->socket, (uint8_t *) &pLLAdp->pDNSMem->dnsDG, pLLAdp->pDNSMem->cbDNSDG, &status); // see if it went out. if(IsIPStatusAnError(status)) { pLLAdp->pDNSMem->dnsState = dnsReady; pLLAdp->pDNSMem->cTry = 0; } else { pLLAdp->pDNSMem->dnsState = dnsWaiting; } break; case dnsWaiting: { uint16_t cbDG = UDPAvailable(&pLLAdp->pDNSMem->socket) ; IPSTATUS status; if(cbDG > sizeof(DNSHDR)) { // we need some space for the datagram uint8_t rgbDNSDG[DNSMAXUDPSIZE]; DNSDG * pDNSDG = (DNSDG *) rgbDNSDG; uint8_t * pEnd = NULL; uint8_t * pCName = NULL; DNSRR * pDNSRRA = NULL; // read the DNS datagram cbDG = UDPRead(&pLLAdp->pDNSMem->socket, rgbDNSDG, DNSMAXUDPSIZE, &status); // There are some servers setting this and they should not! RFC 6195 2.1 and I need to ignore it // Plus this is used internally to determine if we are in machine or network order. This is in network order right now. pDNSDG->dnsHdr.Z = 0; // now put in machine order. cbDG = ExDNSDG(pDNSDG, cbDG); pEnd = rgbDNSDG + cbDG; // if this is not a my response, keep waiting // remember the ID is not exchanged, so network and machine order will have the same ID if(!pDNSDG->dnsHdr.QR || pDNSDG->dnsHdr.ID != pLLAdp->pDNSMem->dnsDG.dnsHdr.ID) { break; } // ops and error occured, jump to finish with error // go to the error state with no address found if(pDNSDG->dnsHdr.RCODE != DNSRCODENoError) { // this is a failure case; go to the next DNS server pLLAdp->pDNSMem->dnsState = dnsSend; break; } // get the canonical name for what we are looking for // the first record is the question which has the name we used. // we need to use the one in this datagram because we do memory range // checking and if we use the QR we sent, it would be out of the memory range pCName = DNSFindCName(pDNSDG, DNSRRAN, pDNSDG->rrRecords, pEnd); // now find the A record pDNSRRA = DNSFindRR(pDNSDG, NULL, DNSRRAN, DNSTYPEA, DNSCLASSIN, pCName, pEnd); // if we got the A record we are done if( pDNSRRA != NULL && ((ILIsIPv6(pLLAdp) && pDNSRRA->RDLENGTH == sizeof(IPv6)) || (!ILIsIPv6(pLLAdp) && pDNSRRA->RDLENGTH == sizeof(IPv4)) ) ) { // copy in our result memcpy(&pLLAdp->pDNSMem->ip, pDNSRRA->RDATA, pDNSRRA->RDLENGTH); // say we are done pLLAdp->pDNSMem->dnsState = dnsReady; pLLAdp->pDNSMem->cTry = 0; // We got an IP so we know this DNS server can work, remember that if(pLLAdp->pDNSMem->iDNSCur < pLLAdp->pDNSMem->dnsNSMax) { pLLAdp->pDNSMem->iDNSWorks = pLLAdp->pDNSMem->iDNSCur; } // get out we found it and are done. break; } // by default we will go to the next well know sever unless we pick up a better NS to go to pLLAdp->pDNSMem->dnsState = dnsSend; // we did not find anything, so update our DNS request to use the new CName if(pCName != pDNSDG->rrRecords) { // make the new DNS packet with the CName instead of what we were using uint16_t cbMax = DNSCreateIPv4QueryDN(pLLAdp, rgbDNSDG, pCName, pEnd, &pLLAdp->pDNSMem->dnsDG); pLLAdp->pDNSMem->cbDNSDG = ExDNSDG(&pLLAdp->pDNSMem->dnsDG, cbMax); } // Didn't find an A record // But maybe there is a name server we should try. // If we asked for recursion and go it, no sense working the issue ourselves. // likewise if there are now NS with IP addresses, no sense working the issue if(!(pDNSDG->dnsHdr.RD && pDNSDG->dnsHdr.RA) && pDNSDG->dnsHdr.NSCOUNT > 0 && pDNSDG->dnsHdr.ARCOUNT > 0) { // this DNS server gave us something, so remember it if(pLLAdp->pDNSMem->iDNSCur < pLLAdp->pDNSMem->dnsNSMax) { pLLAdp->pDNSMem->iDNSWorks = pLLAdp->pDNSMem->iDNSCur; } pDNSRRA = DNSFindNSARR(pDNSDG, pCName, pEnd, DNSTYPEA); // if this looks like a good IP address to try, lets contact that name server if(pDNSRRA != NULL && pDNSRRA->RDLENGTH == sizeof(IPv4)) { // say were are not using one of our well known DNS servers pLLAdp->pDNSMem->iDNSCur = DNSiInvalid; // Put the IP in our socket memcpy(&pLLAdp->pDNSMem->socket.s.ipRemote, pDNSRRA->RDATA, pDNSRRA->RDLENGTH); pLLAdp->pDNSMem->dnsState = dnsReadySend; } } else if(pLLAdp->pDNSMem->iDNSWorks == pLLAdp->pDNSMem->iDNSCur) { pLLAdp->pDNSMem->iDNSWorks = DNSiInvalid; } } else if((SYSGetMilliSecond() - pLLAdp->pDNSMem->tStart) >= pLLAdp->pDNSMem->tTimeout) { pLLAdp->pDNSMem->dnsState = dnsWaitTry; } } break; case dnsWaitTry: if(pLLAdp->pDNSMem->iDNSWorks == pLLAdp->pDNSMem->iDNSCur) { pLLAdp->pDNSMem->iDNSWorks = DNSiInvalid; } if((pLLAdp->pDNSMem->cTry % DNSMINTRY) == 0) { pLLAdp->pDNSMem->dnsState = dnsSend; } else { pLLAdp->pDNSMem->dnsState = dnsReadySend; } break; case dnsReady: pLLAdp->pDNSMem->cTry = 0; break; // noting to do here, either we have something in the DNS memory IP or not. case dnsRedirect: case dnsUninit: default: break; } }
HSOCKET UDPOpenWithSocket(const LLADP * pLLAdp, UDPSOCKET * pSocket, HPMGR hPMGR, const void * pIPvXDest, uint16_t portRemote, uint16_t portLocal, IPSTATUS * pStatus) { // uint32_t portPair = 0; IPSTATUS status = ipsSuccess; SMGR * pSMGR = NULL; uint32_t cb = 0; if(pLLAdp == NULL) { status = ipsAdaptorMustBeSpecified; } else if(pSocket == NULL) { status = ipsSocketNULL; } else if(hPMGR == NULL) { status = ipsNoPMGRGiven; } else if(pIPvXDest == NULL) { status = ipsIPAddressIsNull; } // get the local port else if(portLocal == portDynamicallyAssign) { portLocal = GetEphemeralPort(&g_ffptListeningUDPSockets, &g_nextUDPEphemeralPort); } AssignStatusSafely(pStatus, status); if(IsIPStatusAnError(status)) { return(NULL); } // build the stream manager to point to the page handler // first build the stream to the RxStream pSocket->hPMGR = hPMGR; if(SMGRInit(&pSocket->smgrRxBuff, cbMAXUDPSreamRecord, hPMGR) != (HSMGR) &pSocket->smgrRxBuff) { status = ispOutOfMemory; } else if((pSMGR = alloca((cb = GetSMGRSize(((PMGR *) hPMGR)->cPages)))) == NULL) { status = ispOutOfMemory; } else if(SMGRInit(pSMGR, cb, hPMGR) == NULL) { status = ispOutOfMemory; } else if(SMGRWrite(&pSocket->smgrRxBuff, 0, pSMGR, cb) != cb) { status = ispOutOfMemory; } AssignStatusSafely(pStatus, status); if(IsIPStatusAnError(status)) { SMGRFree((HSMGR) pSMGR); SMGRFree((HSMGR) &pSocket->smgrRxBuff); return(NULL); } pSocket->s.pLLAdp = pLLAdp; pSocket->s.portRemote = portRemote; pSocket->s.portLocal = portLocal; memcpy(&pSocket->s.ipRemote, pIPvXDest, ILIPSize(pLLAdp)); pSocket->cbRxSMGR = cb; // put on the listening list. FFInPacket(&g_ffptListeningUDPSockets, pSocket); // return the socket return(pSocket); }
bool UDPRawSend(const LLADP * pLLAdp, IPSTACK * pIpStack, const void * pIPvXDest, uint16_t portDest, uint16_t portSrc, const uint8_t * pDatagram, uint32_t cbDatagram, bool fFreeDatagramImmediately, IPSTATUS * pStatus) { bool fRet = false; IPSTATUS status = ipsSuccess; if(pLLAdp == NULL) { status = ipsAdaptorMustBeSpecified; } else if(pIpStack == NULL) { status = ipsIpStackNULL; } else if(pIPvXDest == NULL) { status = ipsIPAddressIsNull; } else if(pDatagram == NULL) { status = ipsUDPNullDatagram; } // sometimes we want to keep the actual buffer we passed in the IpStack // because we will later want to reuse it and don't want it copied and freed // the downside is, we have to know that the send is complete somehow; typically // would do this if you don't reuse the buffer until we get a response from the // remote and thus know we must have sent the datagram already. // this is typically NOT the way we would use this, but DHCP Does this extensively else if(!fFreeDatagramImmediately) { pIpStack->pPayload = (void *) pDatagram; } else if(IPSGetPayloadFromAdaptor(pIpStack, cbDatagram) == cbDatagram) { // this is ugly to have to copy the data but somewhat required // the problem is, when we send we may need to wait while an APR is proformed and that // will require that the pDatagram be constant, however on return from this funciton // the application may immediately reuse the memory to construct the next datagram memcpy(pIpStack->pPayload, pDatagram, cbDatagram); } else { status = ispOutOfMemory; } if(IsIPStatusAnError(status)) { AssignStatusSafely(pStatus, status); return(false); } pIpStack->pUDPHdr->portSrc = portSrc; pIpStack->pUDPHdr->portDest = portDest; pIpStack->pUDPHdr->cbHdrData += cbDatagram; pIpStack->cbPayload = cbDatagram; if(ILIsIPv6(pLLAdp)) { pIpStack->pIPv6Hdr->cbPayload = pIpStack->cbPayload; memcpy(&pIpStack->pIPv6Hdr->ipDest, pIPvXDest, sizeof(IPv6)); } else { pIpStack->pIPv4Hdr->cbTotal += pIpStack->cbPayload; memcpy(&pIpStack->pIPv4Hdr->ipDest, pIPvXDest, sizeof(IPv4)); } fRet = ILSend(pIpStack, pStatus); return(fRet); }
/*** void ProcessServer() * * Parameters: * None * * Return Values: * None * * Description: * * This is the main server loop. It: * 1. Scans for WiFi connections * 2. Connects to a WiFi by SSID * 3. Optionally creates a server IP on the detected subnet and dynamically assigns DNS and subnets. * 4. Or uses the static IP you assign; then you must supply DNS and subnet * 5. Starts listening on the supplied server port. * 6. Accepts client connections * 7. Schedules the processing on client connections in a round robin yet fashion (cooperative execution). * 8. Automatically restart if the network goes down * * This illistrates how to write a state machine like loop * so that the PeriodicTask is called everytime through the loop * so the stack stay alive and responsive. * * In the loop we listen for a request, verify it to a limited degree * and then broadcast the Magic Packet to wake the request machine. * * ------------------------------------------------------------ */ void ProcessServer(void) { uint32_t i = 0; // see if we exceeded our timeout value. // then just be done and close the socket // by default, a closed client is never connected // so it is safe to call isConnected() even if it is closed if(stateTimeOut != NONE && TooMuchTime()) { xil_printf("Timeout occured\r\n"); state = stateTimeOut; stateTimeOut = NONE; stateNext = RESTARTREST; } switch(state) { #if defined(USING_WIFI) case WIFISCAN: if(deIPcK.wfScan(&cNetworks, &status)) { xil_printf("Scan Done, %d Networks Found\r\n", cNetworks); state = PRINTAPINFO; RestartTimer(); iNetwork = 0; } else if(IsIPStatusAnError(status)) { xil_printf("Scan Failed\r\n\n"); state = WIFICONNECT; RestartTimer(); } break; case PRINTAPINFO: if(iNetwork < cNetworks) { SCANINFO scanInfo; uint32_t j = 0; // this is MRF24 specific code // this will not run in all implemenations #if MRFVERSION { t_deviceInfo dvInfo; WF_DeviceInfoGet(&dvInfo); Serial.println("Device Info"); Serial.print("DeviceType: 0x"); Serial.print((int) dvInfo.deviceType, HEX); Serial.print(" Rom Version: 0x"); Serial.print((int) dvInfo.romVersion, HEX); Serial.print(" Patch Version: 0x"); Serial.println((int) dvInfo.patchVersion, HEX); } #endif if(deIPcK.getScanInfo(iNetwork, &scanInfo)) { xil_printf("\r\nScan info for index: %d\r\n", iNetwork); xil_printf("SSID: %s\r\n", scanInfo.ssid); xil_printf("\r\nBSSID / MAC: "); for(j=0; j<sizeof(scanInfo.bssid); j++) { if(scanInfo.bssid[j] < 16) { xil_printf("0"); } xil_printf("%X",scanInfo.bssid[j]); } xil_printf("\r\nChannel: %d\r\nSignal Strength: %d\r\n", scanInfo.channel, scanInfo.rssi); if(scanInfo.bssType == DEWF_INFRASTRUCTURE) { xil_printf("Infrastructure Network\r\n"); } else if(scanInfo.bssType == DEWF_ADHOC) { xil_printf("AdHoc Network\r\n"); } else { xil_printf("Unknown Network Type\r\n"); } xil_printf("Beacon Period: %d\r\ndtimPeriod: %d\r\natimWindow: %d\r\n", scanInfo.beaconPeriod, scanInfo.dtimPeriod, scanInfo.atimWindow); xil_printf("Security info: WPA2 WPA Preamble Privacy Reserved Reserved Reserved IE\r\n"); xil_printf(" %d %d %d %d %d %d %d %d\r\n",(scanInfo.apConfig & 0b10000000) >> 7,(scanInfo.apConfig & 0b01000000) >> 6,(scanInfo.apConfig & 0b00100000) >> 5,(scanInfo.apConfig & 0b00010000) >> 4,(scanInfo.apConfig & 0b00001000) >> 3,(scanInfo.apConfig & 0b00000100) >> 2,(scanInfo.apConfig & 0b00000010) >> 1,(scanInfo.apConfig & 0b00000001)); // Serial.print((scanInfo.apConfig & 0b10000000) >> 7, DEC); // Serial.print(" "); // Serial.print((scanInfo.apConfig & 0b01000000) >> 6, DEC); // Serial.print(" "); // Serial.print((scanInfo.apConfig & 0b00100000) >> 5, DEC); // Serial.print(" "); // Serial.print((scanInfo.apConfig & 0b00010000) >> 4, DEC); // Serial.print(" "); // Serial.print((scanInfo.apConfig & 0b00001000) >> 3, DEC); // Serial.print(" "); // Serial.print((scanInfo.apConfig & 0b00000100) >> 2, DEC); // Serial.print(" "); // Serial.print((scanInfo.apConfig & 0b00000010) >> 1, DEC); // Serial.print(" "); // Serial.println((scanInfo.apConfig & 0b00000001), DEC); xil_printf("Count of support bit rates: %d\r\n", scanInfo.cBasicRates); xil_printf("Supported Rates: "); for( j= 0; j< scanInfo.cBasicRates; j++) { uint32_t rate = (scanInfo.basicRateSet[j] & 0x7F) * 500; xil_printf("\t%d kbps", rate); } xil_printf("\r\n"); } else {