Example program with HTTPServer and sensor data streaming over TCPSockets, using Donatien Garnier's Net APIs and services code on top of LWIP. Files StreamServer.h and .cpp encapsulate streaming over TCPSockets. Broadcast is done by sendToAll(), and all incoming data is echoed back to the client. Echo code can be replaced with some remote control of the streaming interface. See main() that shows how to periodically send some data to all subscribed clients. To subscribe, a client should open a socket at <mbed_ip> port 123. I used few lines in TCL code to set up a quick sink for the data. HTTP files are served on port 80 concurrently to the streaming.

Dependencies:   mbed

Committer:
iva2k
Date:
Mon Jun 14 03:24:33 2010 +0000
Revision:
1:3ee499525aa5
Parent:
0:e614f7875b60

        

Who changed what in which revision?

UserRevisionLine numberNew contents of line
iva2k 0:e614f7875b60 1
iva2k 0:e614f7875b60 2 /*
iva2k 0:e614f7875b60 3 Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
iva2k 0:e614f7875b60 4
iva2k 0:e614f7875b60 5 Permission is hereby granted, free of charge, to any person obtaining a copy
iva2k 0:e614f7875b60 6 of this software and associated documentation files (the "Software"), to deal
iva2k 0:e614f7875b60 7 in the Software without restriction, including without limitation the rights
iva2k 0:e614f7875b60 8 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
iva2k 0:e614f7875b60 9 copies of the Software, and to permit persons to whom the Software is
iva2k 0:e614f7875b60 10 furnished to do so, subject to the following conditions:
iva2k 0:e614f7875b60 11
iva2k 0:e614f7875b60 12 The above copyright notice and this permission notice shall be included in
iva2k 0:e614f7875b60 13 all copies or substantial portions of the Software.
iva2k 0:e614f7875b60 14
iva2k 0:e614f7875b60 15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
iva2k 0:e614f7875b60 16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
iva2k 0:e614f7875b60 17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
iva2k 0:e614f7875b60 18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
iva2k 0:e614f7875b60 19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
iva2k 0:e614f7875b60 20 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
iva2k 0:e614f7875b60 21 THE SOFTWARE.
iva2k 0:e614f7875b60 22 */
iva2k 0:e614f7875b60 23
iva2k 0:e614f7875b60 24 #include "MySQLClient.h"
iva2k 0:e614f7875b60 25 #include "sha1.h" //For 4.1+ passwords
iva2k 0:e614f7875b60 26 #include "mycrypt.h" //For 4.0- passwords
iva2k 0:e614f7875b60 27
iva2k 0:e614f7875b60 28 #define __DEBUG
iva2k 0:e614f7875b60 29 #include "dbg/dbg.h"
iva2k 0:e614f7875b60 30
iva2k 0:e614f7875b60 31 #define MYSQL_TIMEOUT_MS 15000
iva2k 0:e614f7875b60 32 #define MYSQL_PORT 3306
iva2k 0:e614f7875b60 33
iva2k 0:e614f7875b60 34 #define BUF_SIZE 256
iva2k 0:e614f7875b60 35
iva2k 0:e614f7875b60 36 #define CLIENT_LONG_PASSWORD 1
iva2k 0:e614f7875b60 37 #define CLIENT_CONNECT_WITH_DB 8
iva2k 0:e614f7875b60 38 #define CLIENT_PROTOCOL_41 512
iva2k 0:e614f7875b60 39 #define CLIENT_INTERACTIVE 1024
iva2k 0:e614f7875b60 40 #define CLIENT_SECURE_CONNECTION 32768
iva2k 0:e614f7875b60 41
iva2k 0:e614f7875b60 42 #define MIN(a,b) ((a)<(b)?(a):(b))
iva2k 0:e614f7875b60 43 #define ABS(a) (((a)>0)?(a):0)
iva2k 0:e614f7875b60 44
iva2k 0:e614f7875b60 45 //MySQL commands
iva2k 0:e614f7875b60 46 #define COM_QUIT 0x01 //Exit
iva2k 0:e614f7875b60 47 #define COM_QUERY 0x03 //Execute an SQL query
iva2k 0:e614f7875b60 48
iva2k 0:e614f7875b60 49 //#define htons( x ) ( (( x << 8 ) & 0xFF00) | (( x >> 8 ) & 0x00FF) )
iva2k 0:e614f7875b60 50 #define ntohs( x ) (htons(x))
iva2k 0:e614f7875b60 51
iva2k 0:e614f7875b60 52 /*#define htonl( x ) ( (( x << 24 ) & 0xFF000000) \
iva2k 0:e614f7875b60 53 | (( x << 8 ) & 0x00FF0000) \
iva2k 0:e614f7875b60 54 | (( x >> 8 ) & 0x0000FF00) \
iva2k 0:e614f7875b60 55 | (( x >> 24 ) & 0x000000FF) )*/
iva2k 0:e614f7875b60 56 #define htonl( x ) (x)
iva2k 0:e614f7875b60 57 #define ntohl( x ) (htonl(x))
iva2k 0:e614f7875b60 58
iva2k 0:e614f7875b60 59 MySQLClient::MySQLClient() : NetService(false) /*Not owned by the pool*/, m_pCbItem(NULL), m_pCbMeth(NULL), m_pCb(NULL),
iva2k 0:e614f7875b60 60 m_pTCPSocket(NULL), m_watchdog(), m_timeout(MYSQL_TIMEOUT_MS*1000), m_pDnsReq(NULL), m_closed(true),
iva2k 0:e614f7875b60 61 m_host(), m_user(), m_password(), m_db(), m_state(MYSQL_CLOSED)
iva2k 0:e614f7875b60 62 {
iva2k 0:e614f7875b60 63 m_buf = new byte[BUF_SIZE];
iva2k 0:e614f7875b60 64 m_pPos = m_buf;
iva2k 0:e614f7875b60 65 m_len = 0;
iva2k 0:e614f7875b60 66 m_size = BUF_SIZE;
iva2k 0:e614f7875b60 67 }
iva2k 0:e614f7875b60 68
iva2k 0:e614f7875b60 69 MySQLClient::~MySQLClient()
iva2k 0:e614f7875b60 70 {
iva2k 0:e614f7875b60 71 close();
iva2k 0:e614f7875b60 72 delete[] m_buf;
iva2k 0:e614f7875b60 73 }
iva2k 0:e614f7875b60 74
iva2k 0:e614f7875b60 75 //High Level setup functions
iva2k 0:e614f7875b60 76 MySQLResult MySQLClient::open(Host& host, const string& user, const string& password, const string& db, void (*pMethod)(MySQLResult)) //Non blocking
iva2k 0:e614f7875b60 77 {
iva2k 0:e614f7875b60 78 setOnResult(pMethod);
iva2k 0:e614f7875b60 79 setup(host, user, password, db);
iva2k 0:e614f7875b60 80 return MYSQL_PROCESSING;
iva2k 0:e614f7875b60 81 }
iva2k 0:e614f7875b60 82
iva2k 0:e614f7875b60 83 #if 0 //Ref only
iva2k 0:e614f7875b60 84 template<class T>
iva2k 0:e614f7875b60 85 MySQLResult MySQLClient::open(Host& host, const string& user, const string& password, const string& db, T* pItem, void (T::*pMethod)(MySQLResult)) //Non blocking
iva2k 0:e614f7875b60 86 {
iva2k 0:e614f7875b60 87 setOnResult(pItem, pMethod);
iva2k 0:e614f7875b60 88 setup(host, user, password, db);
iva2k 0:e614f7875b60 89 return MYSQL_PROCESSING;
iva2k 0:e614f7875b60 90 }
iva2k 0:e614f7875b60 91 #endif
iva2k 0:e614f7875b60 92
iva2k 0:e614f7875b60 93 MySQLResult MySQLClient::sql(string& sqlCommand)
iva2k 0:e614f7875b60 94 {
iva2k 0:e614f7875b60 95 if(m_state!=MYSQL_COMMANDS)
iva2k 0:e614f7875b60 96 return MYSQL_SETUP;
iva2k 0:e614f7875b60 97 sendCommand(COM_QUERY, (byte*)sqlCommand.data(), sqlCommand.length());
iva2k 0:e614f7875b60 98 return MYSQL_PROCESSING;
iva2k 0:e614f7875b60 99 }
iva2k 0:e614f7875b60 100
iva2k 0:e614f7875b60 101 MySQLResult MySQLClient::exit()
iva2k 0:e614f7875b60 102 {
iva2k 0:e614f7875b60 103 sendCommand(COM_QUIT, NULL, 0);
iva2k 0:e614f7875b60 104 close();
iva2k 0:e614f7875b60 105 return MYSQL_OK;
iva2k 0:e614f7875b60 106 }
iva2k 0:e614f7875b60 107
iva2k 0:e614f7875b60 108 void MySQLClient::setOnResult( void (*pMethod)(MySQLResult) )
iva2k 0:e614f7875b60 109 {
iva2k 0:e614f7875b60 110 m_pCb = pMethod;
iva2k 0:e614f7875b60 111 m_pCbItem = NULL;
iva2k 0:e614f7875b60 112 m_pCbMeth = NULL;
iva2k 0:e614f7875b60 113 }
iva2k 0:e614f7875b60 114
iva2k 0:e614f7875b60 115 #if 0 //Ref only
iva2k 0:e614f7875b60 116 template<class T>
iva2k 0:e614f7875b60 117 void MySQLClient::setOnResult( T* pItem, void (T::*pMethod)(MySQLResult) )
iva2k 0:e614f7875b60 118 {
iva2k 0:e614f7875b60 119 m_pCb = NULL;
iva2k 0:e614f7875b60 120 m_pCbItem = (CDummy*) pItem;
iva2k 0:e614f7875b60 121 m_pCbMeth = (void (CDummy::*)(MySQLResult)) pMethod;
iva2k 0:e614f7875b60 122 }
iva2k 0:e614f7875b60 123 #endif
iva2k 0:e614f7875b60 124
iva2k 0:e614f7875b60 125 void MySQLClient::setTimeout(int ms)
iva2k 0:e614f7875b60 126 {
iva2k 0:e614f7875b60 127 m_timeout = 1000*ms;
iva2k 0:e614f7875b60 128 }
iva2k 0:e614f7875b60 129
iva2k 0:e614f7875b60 130 void MySQLClient::poll() //Called by NetServices
iva2k 0:e614f7875b60 131 {
iva2k 0:e614f7875b60 132 if(m_closed)
iva2k 0:e614f7875b60 133 {
iva2k 0:e614f7875b60 134 return;
iva2k 0:e614f7875b60 135 }
iva2k 0:e614f7875b60 136 if(m_watchdog.read_us()>m_timeout)
iva2k 0:e614f7875b60 137 {
iva2k 0:e614f7875b60 138 onTimeout();
iva2k 0:e614f7875b60 139 }
iva2k 0:e614f7875b60 140 }
iva2k 0:e614f7875b60 141
iva2k 0:e614f7875b60 142 void MySQLClient::resetTimeout()
iva2k 0:e614f7875b60 143 {
iva2k 0:e614f7875b60 144 m_watchdog.reset();
iva2k 0:e614f7875b60 145 m_watchdog.start();
iva2k 0:e614f7875b60 146 }
iva2k 0:e614f7875b60 147
iva2k 0:e614f7875b60 148 void MySQLClient::init()
iva2k 0:e614f7875b60 149 {
iva2k 0:e614f7875b60 150 close(); //Remove previous elements
iva2k 0:e614f7875b60 151 if(!m_closed) //Already opened
iva2k 0:e614f7875b60 152 return;
iva2k 0:e614f7875b60 153 m_state = MYSQL_HANDSHAKE;
iva2k 0:e614f7875b60 154 m_pTCPSocket = new TCPSocket;
iva2k 0:e614f7875b60 155 m_pTCPSocket->setOnEvent(this, &MySQLClient::onTCPSocketEvent);
iva2k 0:e614f7875b60 156 m_closed = false;
iva2k 0:e614f7875b60 157 }
iva2k 0:e614f7875b60 158
iva2k 0:e614f7875b60 159 void MySQLClient::close()
iva2k 0:e614f7875b60 160 {
iva2k 0:e614f7875b60 161 if(m_closed)
iva2k 0:e614f7875b60 162 return;
iva2k 0:e614f7875b60 163 m_state = MYSQL_CLOSED;
iva2k 0:e614f7875b60 164 m_closed = true; //Prevent recursive calling or calling on an object being destructed by someone else
iva2k 0:e614f7875b60 165 m_watchdog.stop(); //Stop timeout
iva2k 0:e614f7875b60 166 m_watchdog.reset();
iva2k 0:e614f7875b60 167 m_pTCPSocket->resetOnEvent();
iva2k 0:e614f7875b60 168 m_pTCPSocket->close();
iva2k 0:e614f7875b60 169 delete m_pTCPSocket;
iva2k 0:e614f7875b60 170 m_pTCPSocket = NULL;
iva2k 0:e614f7875b60 171 if( m_pDnsReq )
iva2k 0:e614f7875b60 172 {
iva2k 0:e614f7875b60 173 m_pDnsReq->close();
iva2k 0:e614f7875b60 174 delete m_pDnsReq;
iva2k 0:e614f7875b60 175 m_pDnsReq = NULL;
iva2k 0:e614f7875b60 176 }
iva2k 0:e614f7875b60 177 }
iva2k 0:e614f7875b60 178
iva2k 0:e614f7875b60 179 void MySQLClient::setup(Host& host, const string& user, const string& password, const string& db) //Setup connection, make DNS Req if necessary
iva2k 0:e614f7875b60 180 {
iva2k 0:e614f7875b60 181 init(); //Initialize client in known state, create socket
iva2k 0:e614f7875b60 182 resetTimeout();
iva2k 0:e614f7875b60 183 m_host = host;
iva2k 0:e614f7875b60 184 if(!host.getPort())
iva2k 0:e614f7875b60 185 host.setPort( MYSQL_PORT ); //Default port
iva2k 0:e614f7875b60 186
iva2k 0:e614f7875b60 187 m_user = user;
iva2k 0:e614f7875b60 188 m_password = password;
iva2k 0:e614f7875b60 189
iva2k 0:e614f7875b60 190 m_db = db;
iva2k 0:e614f7875b60 191
iva2k 0:e614f7875b60 192 IpAddr ip;
iva2k 0:e614f7875b60 193 if( !host.getIp().isNull() )
iva2k 0:e614f7875b60 194 {
iva2k 0:e614f7875b60 195 connect();
iva2k 0:e614f7875b60 196 }
iva2k 0:e614f7875b60 197 else //Need to do a DNS Query...
iva2k 0:e614f7875b60 198 {
iva2k 1:3ee499525aa5 199 DBG("DNS Query...\r\n");
iva2k 0:e614f7875b60 200 m_pDnsReq = new DNSRequest();
iva2k 0:e614f7875b60 201 m_pDnsReq->setOnReply(this, &MySQLClient::onDNSReply);
iva2k 0:e614f7875b60 202 m_pDnsReq->resolve(&m_host);
iva2k 1:3ee499525aa5 203 DBG("MySQLClient : DNSRequest %p\r\n", m_pDnsReq);
iva2k 0:e614f7875b60 204 }
iva2k 0:e614f7875b60 205 }
iva2k 0:e614f7875b60 206
iva2k 0:e614f7875b60 207 void MySQLClient::connect() //Start Connection
iva2k 0:e614f7875b60 208 {
iva2k 0:e614f7875b60 209 resetTimeout();
iva2k 1:3ee499525aa5 210 DBG("Connecting...\r\n");
iva2k 0:e614f7875b60 211 m_pTCPSocket->connect(m_host);
iva2k 0:e614f7875b60 212 m_packetId = 0;
iva2k 0:e614f7875b60 213 }
iva2k 0:e614f7875b60 214
iva2k 0:e614f7875b60 215 void MySQLClient::handleHandshake()
iva2k 0:e614f7875b60 216 {
iva2k 0:e614f7875b60 217 readData();
iva2k 0:e614f7875b60 218 if( ! (( m_len > 1 ) && ( memchr( m_buf + 1, 0, m_len ) != NULL )) )
iva2k 0:e614f7875b60 219 {
iva2k 0:e614f7875b60 220 DBG("Connected but could not find pcsz...\n");
iva2k 0:e614f7875b60 221 onResult(MYSQL_PRTCL);
iva2k 0:e614f7875b60 222 return;
iva2k 0:e614f7875b60 223 }
iva2k 0:e614f7875b60 224
iva2k 0:e614f7875b60 225 DBG("Connected to server: %d bytes read ; Protocol version %d, mysql-%s.\n", m_len, m_buf[0], &m_buf[1]);
iva2k 0:e614f7875b60 226
iva2k 0:e614f7875b60 227 m_pPos = (byte*) memchr( (char*)(m_buf + 1), 0, m_len ) + 1;
iva2k 0:e614f7875b60 228
iva2k 0:e614f7875b60 229 sendAuth();
iva2k 0:e614f7875b60 230 }
iva2k 0:e614f7875b60 231
iva2k 0:e614f7875b60 232 void MySQLClient::sendAuth()
iva2k 0:e614f7875b60 233 {
iva2k 0:e614f7875b60 234 if( m_len - (m_pPos - m_buf) != 44)
iva2k 0:e614f7875b60 235 {
iva2k 0:e614f7875b60 236 //We only support protocol >= mysql-4.1
iva2k 0:e614f7875b60 237 DBG("Message after pcsz has wrong len (%d != 44)...\n", m_len - (m_pPos - m_buf));
iva2k 0:e614f7875b60 238 onResult(MYSQL_PRTCL);
iva2k 0:e614f7875b60 239 return;
iva2k 0:e614f7875b60 240 }
iva2k 0:e614f7875b60 241
iva2k 0:e614f7875b60 242 uint16_t serverFlags = *((uint16_t*)&m_pPos[13]);
iva2k 0:e614f7875b60 243 DBG("Server capabilities are %04X.\n", serverFlags);
iva2k 0:e614f7875b60 244
iva2k 0:e614f7875b60 245 uint32_t clientFlags = CLIENT_CONNECT_WITH_DB | CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION | CLIENT_INTERACTIVE;;
iva2k 0:e614f7875b60 246
iva2k 0:e614f7875b60 247 //if(serverFlags & CLIENT_LONG_PASSWORD)
iva2k 0:e614f7875b60 248
iva2k 0:e614f7875b60 249 DBG("Using auth 4.1+\n");
iva2k 0:e614f7875b60 250 //Encrypt pw using scramble
iva2k 0:e614f7875b60 251 byte scramble[20+20]={0};
iva2k 0:e614f7875b60 252 memcpy(scramble, m_pPos+4, 8);
iva2k 0:e614f7875b60 253 memcpy(scramble+8, m_pPos+31, 12); // *(m_pPos+43) == 0 (zero-terminated char*)
iva2k 0:e614f7875b60 254
iva2k 0:e614f7875b60 255 byte stage1_hash[20] = {0};
iva2k 0:e614f7875b60 256 sha1( (byte*)m_password.data(), m_password.length(), stage1_hash );
iva2k 0:e614f7875b60 257
iva2k 0:e614f7875b60 258 sha1( stage1_hash, 20, ((byte*)scramble + 20) );
iva2k 0:e614f7875b60 259
iva2k 0:e614f7875b60 260 byte token[20] = {0};
iva2k 0:e614f7875b60 261 sha1( scramble, 40, token );
iva2k 0:e614f7875b60 262
iva2k 0:e614f7875b60 263 for(int i=0;i<20;i++)
iva2k 0:e614f7875b60 264 token[i] = token[i] ^ stage1_hash[i];
iva2k 0:e614f7875b60 265
iva2k 0:e614f7875b60 266 clientFlags |= CLIENT_LONG_PASSWORD;
iva2k 0:e614f7875b60 267
iva2k 0:e614f7875b60 268 DBG("Building response\n");
iva2k 0:e614f7875b60 269 //Build response
iva2k 0:e614f7875b60 270
iva2k 0:e614f7875b60 271 //BE
iva2k 0:e614f7875b60 272 *((uint32_t*)&m_buf[0]) = htonl(clientFlags);
iva2k 0:e614f7875b60 273 *((uint32_t*)&m_buf[4]) = BUF_SIZE; //Max packets size
iva2k 0:e614f7875b60 274 m_buf[8] = 8; //latin1 charset
iva2k 0:e614f7875b60 275 memset((char*)(m_buf+9),0,23);
iva2k 0:e614f7875b60 276 strcpy((char*)(m_buf+32),m_user.c_str());
iva2k 0:e614f7875b60 277 m_pPos = m_buf + 32 + m_user.length() + 1;
iva2k 0:e614f7875b60 278 m_pPos[0] = 20;
iva2k 0:e614f7875b60 279 memcpy((char*)&m_pPos[1],token,20);
iva2k 0:e614f7875b60 280 strcpy((char*)(m_pPos+21),m_db.c_str());
iva2k 0:e614f7875b60 281 m_len = 32 + m_user.length() + 1 + 21 + m_db.length() + 1;
iva2k 0:e614f7875b60 282
iva2k 0:e614f7875b60 283 //Save first part of scramble in case we need it again
iva2k 0:e614f7875b60 284 memcpy(&m_buf[BUF_SIZE-8], scramble, 8);
iva2k 0:e614f7875b60 285
iva2k 0:e614f7875b60 286 m_state = MYSQL_AUTH;
iva2k 0:e614f7875b60 287
iva2k 0:e614f7875b60 288 DBG("Writing data\n");
iva2k 0:e614f7875b60 289 writeData();
iva2k 0:e614f7875b60 290 }
iva2k 0:e614f7875b60 291
iva2k 0:e614f7875b60 292 void MySQLClient::handleAuthResult()
iva2k 0:e614f7875b60 293 {
iva2k 0:e614f7875b60 294 readData();
iva2k 0:e614f7875b60 295 if(m_len==1 && *m_buf==0xfe)
iva2k 0:e614f7875b60 296 {
iva2k 0:e614f7875b60 297 //Re-send auth using 4.0- auth
iva2k 0:e614f7875b60 298 sendAuth323();
iva2k 0:e614f7875b60 299 return;
iva2k 0:e614f7875b60 300 }
iva2k 0:e614f7875b60 301 m_watchdog.stop(); //Stop timeout
iva2k 0:e614f7875b60 302 m_watchdog.reset();
iva2k 0:e614f7875b60 303 if(m_len<2)
iva2k 0:e614f7875b60 304 {
iva2k 0:e614f7875b60 305 DBG("Response too short..\n");
iva2k 0:e614f7875b60 306 onResult(MYSQL_PRTCL);
iva2k 0:e614f7875b60 307 return;
iva2k 0:e614f7875b60 308 }
iva2k 0:e614f7875b60 309 DBG("RC=%d ",m_buf[0]);
iva2k 0:e614f7875b60 310 if(m_buf[0]==0)
iva2k 0:e614f7875b60 311 {
iva2k 0:e614f7875b60 312 m_buf[m_len] = 0;
iva2k 0:e614f7875b60 313 m_pPos = m_buf + 1;
iva2k 0:e614f7875b60 314 m_pPos += m_buf[1] +1;
iva2k 0:e614f7875b60 315 m_pPos += m_pPos[0];
iva2k 0:e614f7875b60 316 m_pPos += 1;
iva2k 0:e614f7875b60 317 DBG("(OK) : Server status %d, Message : %s\n", *((uint16_t*)&m_pPos[0]), m_pPos+4);
iva2k 0:e614f7875b60 318 onResult(MYSQL_OK);
iva2k 0:e614f7875b60 319 }
iva2k 0:e614f7875b60 320 else
iva2k 0:e614f7875b60 321 {
iva2k 0:e614f7875b60 322 m_buf[m_len] = 0;
iva2k 0:e614f7875b60 323 DBG("(Error %d) : %s\n", *((uint16_t*)&m_buf[1]), &m_buf[9]); //LE
iva2k 0:e614f7875b60 324 onResult(MYSQL_AUTHFAILED);
iva2k 0:e614f7875b60 325 return;
iva2k 0:e614f7875b60 326 }
iva2k 0:e614f7875b60 327 m_state = MYSQL_COMMANDS;
iva2k 0:e614f7875b60 328 }
iva2k 0:e614f7875b60 329
iva2k 0:e614f7875b60 330 void MySQLClient::sendAuth323()
iva2k 0:e614f7875b60 331 {
iva2k 0:e614f7875b60 332 DBG("Using auth 4.0-\n");
iva2k 0:e614f7875b60 333 byte scramble[8]={0};
iva2k 0:e614f7875b60 334
iva2k 0:e614f7875b60 335 memcpy(scramble, &m_buf[BUF_SIZE-8], 8); //Recover scramble
iva2k 0:e614f7875b60 336
iva2k 0:e614f7875b60 337 //memcpy(scramble+8, m_pPos+31, 12); // *(m_pPos+43) == 0 (zero-terminated char*)
iva2k 0:e614f7875b60 338
iva2k 0:e614f7875b60 339 byte token[9]={0};
iva2k 0:e614f7875b60 340
iva2k 0:e614f7875b60 341 scramble_323((char*)token, (const char*)scramble, m_password.c_str());
iva2k 0:e614f7875b60 342
iva2k 0:e614f7875b60 343 DBG("Building response\n");
iva2k 0:e614f7875b60 344 //Build response
iva2k 0:e614f7875b60 345
iva2k 0:e614f7875b60 346 memcpy((char*)m_buf,token,9);
iva2k 0:e614f7875b60 347 m_len = 9;
iva2k 0:e614f7875b60 348
iva2k 0:e614f7875b60 349 #if 0
iva2k 0:e614f7875b60 350 *((uint32_t*)&m_buf[0]) = htonl(clientFlags);
iva2k 0:e614f7875b60 351 *((uint32_t*)&m_buf[4]) = BUF_SIZE; //Max packets size
iva2k 0:e614f7875b60 352 m_buf[8] = 8; //latin1 charset
iva2k 0:e614f7875b60 353 memset((char*)(m_buf+9),0,23);
iva2k 0:e614f7875b60 354 strcpy((char*)(m_buf+32),m_user.c_str());
iva2k 0:e614f7875b60 355 m_pPos = m_buf + 32 + m_user.length() + 1;
iva2k 0:e614f7875b60 356 m_pPos[0] = 8;
iva2k 0:e614f7875b60 357 memcpy((char*)&m_pPos[1],token+1,8);
iva2k 0:e614f7875b60 358 strcpy((char*)(m_pPos+9),m_db.c_str());
iva2k 0:e614f7875b60 359 m_len = 32 + m_user.length() + 1 + 9 + m_db.length() + 1;
iva2k 0:e614f7875b60 360 #endif
iva2k 0:e614f7875b60 361
iva2k 0:e614f7875b60 362 DBG("Writing data\n");
iva2k 0:e614f7875b60 363 writeData();
iva2k 0:e614f7875b60 364 }
iva2k 0:e614f7875b60 365
iva2k 0:e614f7875b60 366 void MySQLClient::sendCommand(byte command, byte* arg, int len)
iva2k 0:e614f7875b60 367 {
iva2k 0:e614f7875b60 368 DBG("Sending command %d, payload of len %d\n", command, len);
iva2k 0:e614f7875b60 369 m_packetId=0;//Reset packet ID (New sequence)
iva2k 0:e614f7875b60 370 m_buf[0] = command;
iva2k 0:e614f7875b60 371 memcpy(&m_buf[1], arg, len);
iva2k 0:e614f7875b60 372 m_len = 1 + len;
iva2k 0:e614f7875b60 373 writeData();
iva2k 0:e614f7875b60 374 m_watchdog.start();
iva2k 0:e614f7875b60 375 }
iva2k 0:e614f7875b60 376
iva2k 0:e614f7875b60 377 void MySQLClient::handleCommandResult()
iva2k 0:e614f7875b60 378 {
iva2k 0:e614f7875b60 379 readData();
iva2k 0:e614f7875b60 380 m_watchdog.stop(); //Stop timeout
iva2k 0:e614f7875b60 381 m_watchdog.reset();
iva2k 0:e614f7875b60 382 if(m_len<2)
iva2k 0:e614f7875b60 383 {
iva2k 0:e614f7875b60 384 DBG("Response too short..\n");
iva2k 0:e614f7875b60 385 onResult(MYSQL_PRTCL);
iva2k 0:e614f7875b60 386 return;
iva2k 0:e614f7875b60 387 }
iva2k 0:e614f7875b60 388 DBG("RC=%d ",m_buf[0]);
iva2k 0:e614f7875b60 389 if(m_buf[0]==0)
iva2k 0:e614f7875b60 390 {
iva2k 0:e614f7875b60 391 DBG("(OK)\n");
iva2k 0:e614f7875b60 392 onResult(MYSQL_OK);
iva2k 0:e614f7875b60 393 }
iva2k 0:e614f7875b60 394 else
iva2k 0:e614f7875b60 395 {
iva2k 0:e614f7875b60 396 m_buf[m_len] = 0;
iva2k 0:e614f7875b60 397 DBG("(SQL Error %d) : %s\n", *((uint16_t*)&m_buf[1]), &m_buf[9]); //LE
iva2k 0:e614f7875b60 398 onResult(MYSQL_SQL);
iva2k 0:e614f7875b60 399 return;
iva2k 0:e614f7875b60 400 }
iva2k 0:e614f7875b60 401 }
iva2k 0:e614f7875b60 402
iva2k 0:e614f7875b60 403 void MySQLClient::readData() //Copy to buf
iva2k 0:e614f7875b60 404 {
iva2k 0:e614f7875b60 405 byte head[4];
iva2k 0:e614f7875b60 406 int ret = m_pTCPSocket->recv((char*)head, 4); //Packet header
iva2k 0:e614f7875b60 407 m_len = *((uint16_t*)&head[0]);
iva2k 0:e614f7875b60 408 m_packetId = head[3];
iva2k 0:e614f7875b60 409 DBG("Packet Id %d of length %d\n", head[3], m_len);
iva2k 0:e614f7875b60 410 m_packetId++;
iva2k 0:e614f7875b60 411 if(ret>0)
iva2k 0:e614f7875b60 412 ret = m_pTCPSocket->recv((char*)m_buf, m_len);
iva2k 0:e614f7875b60 413 if(ret < 0)//Error
iva2k 0:e614f7875b60 414 {
iva2k 0:e614f7875b60 415 onResult(MYSQL_CONN);
iva2k 0:e614f7875b60 416 return;
iva2k 0:e614f7875b60 417 }
iva2k 0:e614f7875b60 418 if(ret < m_len)
iva2k 0:e614f7875b60 419 {
iva2k 0:e614f7875b60 420 DBG("WARN: Incomplete packet\n");
iva2k 0:e614f7875b60 421 }
iva2k 0:e614f7875b60 422 m_len = ret;
iva2k 0:e614f7875b60 423 }
iva2k 0:e614f7875b60 424
iva2k 0:e614f7875b60 425 void MySQLClient::writeData() //Copy from buf
iva2k 0:e614f7875b60 426 {
iva2k 0:e614f7875b60 427 byte head[4] = { 0 };
iva2k 0:e614f7875b60 428 *((uint16_t*)&head[0]) = m_len;
iva2k 0:e614f7875b60 429 head[3] = m_packetId;
iva2k 0:e614f7875b60 430 DBG("Packet Id %d\n", head[3]);
iva2k 0:e614f7875b60 431 m_packetId++;
iva2k 0:e614f7875b60 432 int ret = m_pTCPSocket->send((char*)head, 4); //Packet header
iva2k 0:e614f7875b60 433 if(ret>0)
iva2k 0:e614f7875b60 434 ret = m_pTCPSocket->send((char*)m_buf, m_len);
iva2k 0:e614f7875b60 435 if(ret < 0)//Error
iva2k 0:e614f7875b60 436 {
iva2k 0:e614f7875b60 437 onResult(MYSQL_CONN);
iva2k 0:e614f7875b60 438 return;
iva2k 0:e614f7875b60 439 }
iva2k 0:e614f7875b60 440 m_len = 0;//FIXME... incomplete packets handling
iva2k 0:e614f7875b60 441 }
iva2k 0:e614f7875b60 442
iva2k 0:e614f7875b60 443 void MySQLClient::onTCPSocketEvent(TCPSocketEvent e)
iva2k 0:e614f7875b60 444 {
iva2k 1:3ee499525aa5 445 DBG("Event %d in MySQLClient::onTCPSocketEvent()\r\n", e);
iva2k 0:e614f7875b60 446
iva2k 0:e614f7875b60 447 if(m_closed)
iva2k 0:e614f7875b60 448 {
iva2k 1:3ee499525aa5 449 DBG("WARN: Discarded\r\n");
iva2k 0:e614f7875b60 450 return;
iva2k 0:e614f7875b60 451 }
iva2k 0:e614f7875b60 452
iva2k 0:e614f7875b60 453 switch(e)
iva2k 0:e614f7875b60 454 {
iva2k 0:e614f7875b60 455 case TCPSOCKET_READABLE: //Incoming data
iva2k 0:e614f7875b60 456 resetTimeout();
iva2k 0:e614f7875b60 457 if(m_state == MYSQL_HANDSHAKE)
iva2k 0:e614f7875b60 458 handleHandshake();
iva2k 0:e614f7875b60 459 else if(m_state == MYSQL_AUTH)
iva2k 0:e614f7875b60 460 handleAuthResult();
iva2k 0:e614f7875b60 461 else if(m_state == MYSQL_COMMANDS)
iva2k 0:e614f7875b60 462 handleCommandResult();
iva2k 0:e614f7875b60 463 break;
iva2k 0:e614f7875b60 464 case TCPSOCKET_WRITEABLE: //We can send data
iva2k 0:e614f7875b60 465 resetTimeout();
iva2k 0:e614f7875b60 466 break;
iva2k 0:e614f7875b60 467 case TCPSOCKET_CONNECTED: //Connected, wait for handshake packet
iva2k 0:e614f7875b60 468 resetTimeout();
iva2k 0:e614f7875b60 469 break;
iva2k 0:e614f7875b60 470 case TCPSOCKET_CONTIMEOUT:
iva2k 0:e614f7875b60 471 case TCPSOCKET_CONRST:
iva2k 0:e614f7875b60 472 case TCPSOCKET_CONABRT:
iva2k 0:e614f7875b60 473 case TCPSOCKET_ERROR:
iva2k 1:3ee499525aa5 474 DBG("Connection error.\r\n");
iva2k 0:e614f7875b60 475 onResult(MYSQL_CONN);
iva2k 0:e614f7875b60 476 case TCPSOCKET_DISCONNECTED:
iva2k 0:e614f7875b60 477 //There might still be some data available for reading
iva2k 0:e614f7875b60 478 //So if we are in a reading state, do not close the socket yet
iva2k 0:e614f7875b60 479 if(m_state != MYSQL_CLOSED)
iva2k 0:e614f7875b60 480 {
iva2k 0:e614f7875b60 481 onResult(MYSQL_CONN);
iva2k 0:e614f7875b60 482 }
iva2k 1:3ee499525aa5 483 DBG("Connection closed by remote host.\r\n");
iva2k 0:e614f7875b60 484 break;
iva2k 0:e614f7875b60 485 }
iva2k 0:e614f7875b60 486 }
iva2k 0:e614f7875b60 487
iva2k 0:e614f7875b60 488 void MySQLClient::onDNSReply(DNSReply r)
iva2k 0:e614f7875b60 489 {
iva2k 0:e614f7875b60 490 if(m_closed)
iva2k 0:e614f7875b60 491 {
iva2k 1:3ee499525aa5 492 DBG("WARN: Discarded\r\n");
iva2k 0:e614f7875b60 493 return;
iva2k 0:e614f7875b60 494 }
iva2k 0:e614f7875b60 495
iva2k 0:e614f7875b60 496 if( r != DNS_FOUND )
iva2k 0:e614f7875b60 497 {
iva2k 1:3ee499525aa5 498 DBG("Could not resolve hostname.\r\n");
iva2k 0:e614f7875b60 499 onResult(MYSQL_DNS);
iva2k 0:e614f7875b60 500 return;
iva2k 0:e614f7875b60 501 }
iva2k 0:e614f7875b60 502
iva2k 1:3ee499525aa5 503 DBG("DNS Resolved to %d.%d.%d.%d.\r\n",m_host.getIp()[0],m_host.getIp()[1],m_host.getIp()[2],m_host.getIp()[3]);
iva2k 0:e614f7875b60 504 //If no error, m_host has been updated by m_pDnsReq so we're set to go !
iva2k 0:e614f7875b60 505 m_pDnsReq->close();
iva2k 0:e614f7875b60 506 delete m_pDnsReq;
iva2k 0:e614f7875b60 507 m_pDnsReq = NULL;
iva2k 0:e614f7875b60 508 connect();
iva2k 0:e614f7875b60 509 }
iva2k 0:e614f7875b60 510
iva2k 0:e614f7875b60 511 void MySQLClient::onResult(MySQLResult r) //Called when exchange completed or on failure
iva2k 0:e614f7875b60 512 {
iva2k 0:e614f7875b60 513 if(m_pCbItem && m_pCbMeth)
iva2k 0:e614f7875b60 514 (m_pCbItem->*m_pCbMeth)(r);
iva2k 0:e614f7875b60 515 else if(m_pCb)
iva2k 0:e614f7875b60 516 m_pCb(r);
iva2k 0:e614f7875b60 517
iva2k 0:e614f7875b60 518 if( (r==MYSQL_DNS) || (r==MYSQL_PRTCL) || (r==MYSQL_AUTHFAILED) || (r==MYSQL_TIMEOUT) || (r==MYSQL_CONN) ) //Fatal error, close connection
iva2k 0:e614f7875b60 519 close();
iva2k 0:e614f7875b60 520 }
iva2k 0:e614f7875b60 521
iva2k 0:e614f7875b60 522 void MySQLClient::onTimeout() //Connection has timed out
iva2k 0:e614f7875b60 523 {
iva2k 1:3ee499525aa5 524 DBG("Timed out.\n");
iva2k 0:e614f7875b60 525 onResult(MYSQL_DNS);
iva2k 0:e614f7875b60 526 close();
iva2k 0:e614f7875b60 527 }