📄 trut.cpp
字号:
/* * Copyright (c) 2001 Intel Corporation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. Neither the name of the Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. *//* * trut: Trusted Gnutella client. */#include <stdio.h>#include <string.h>#include <ptp/rand.h>#include <ptp/encode.h>#include <ptp/debug.h>#include "trut.h"#define STRLEN_CONST(x) (sizeof(x) - 1)#define STRNCMP_CONST(x, y) strncmp((const char*)(x), (y), sizeof(y) - 1)/* * Trut::SearchContext: Pending search information. */struct Trut::SearchContext:public PTP::List::Entry{ BYTE m_guid[Gnutella::GUID_SIZE]; SearchCallback m_callback; void *m_context; Group *m_group;};/* * Trut::TransferContext: Pending file transfer. */struct Trut::TransferContext{ TransferContext(FILE *f, PTP::Net::Connection *c = NULL) :fp(f), conn(c), callback(NULL), size(0) {} FILE *fp; PTP::Net::Connection *conn; Trut::GetCallback callback; Trut::File *file; void *context; unsigned long size;};/* * Trut::GetContext: Pending file download. */struct Trut::GetContext:public PTP::List::Entry{ GetContext():m_conn(NULL), m_path(NULL) {} ~GetContext() { delete [] m_path; delete m_conn; } Trut *m_trut; PTP::Key *m_key; const PTP::Identity *m_id; PTP::Net::Connection *m_conn; Trut::File *m_file; char *m_path; Trut::GetCallback m_callback; void *m_context; unsigned long m_size; PTP::Thread m_thread;};/* * Trut::PutContext: Pending file upload. */struct Trut::PutContext{ Trut::Host *m_host; BYTE *m_resp; const PTP::Identity *m_id;};/* * Trut::Host::SendGnutella: Send a Gnutella packet. * @pkt: Gnutella packet. */voidTrut::Host::SendGnutella(const BYTE *pkt){ int size = 0; if (STRNCMP_CONST((char*) pkt, "GNUTELLA") != 0) { Gnutella::Packet *p = (Gnutella::Packet *) pkt; size = sizeof(*p) + Gnutella::Get32(p->size); } else size = strlen((char*) pkt); m_conn->WriteAll(pkt, size);}/* * Trut::Host::ReceiveGnutella: Receive a Gnutella packet. * Returns: Gnutella packet or NULL on error. */BYTE *Trut::Host::ReceiveGnutella(){ char buffer[512]; int readsize = m_conn->Read((BYTE*) buffer, sizeof(buffer) - 1); if (readsize <= 0) return NULL; int size = 0; if (STRNCMP_CONST(buffer, GNUTELLA_CONNECT_REQUEST) != 0 && STRNCMP_CONST(buffer, GNUTELLA_CONNECT_RESPONSE) != 0) { Gnutella::Packet *pkt = (Gnutella::Packet*) buffer; size = sizeof(*pkt) + Gnutella::Get32(pkt->size); } else { buffer[readsize] = '\0'; size = readsize; } if (size < 0) return NULL; BYTE *pkt = new BYTE[size]; memcpy(pkt, buffer, readsize); if (size > readsize && m_conn->ReadAll(pkt + readsize, size - readsize) < 0) { delete [] pkt; pkt = NULL; } return pkt;}/** * Trut::Trut: Class constructor. * @store: Certificate store. */Trut::Trut(PTP::Store *store) :m_store(store), m_open(NULL), m_close(NULL){ m_ip = PTP::Net::Lookup("localhost"); m_port = PORT_DEFAULT; m_local = m_store->Find(NULL, 1); m_auth = new PTP::Authenticator(m_store);}/** * Trut::~Trut: Class destructor. */Trut::~Trut(){ delete m_auth; SearchStop(); LeaveAllGroups(); RemoveAllShared(); RemoveAllPorts(); RemoveAllHosts();}/** * Trut::AddPort: Create a new server port. * @port: TCP port number. * Returns: Port connection or NULL on error. */Trut::Port *Trut::AddPort(PTP::Net::Port port){ Port *p = new Port; p->m_trut = this; p->m_conn = new PTP::Net::Connection(PTP::Net::Connection::RAW, port ? port:PORT_DEFAULT); if (p->m_conn->Open()) { delete p; return NULL; } m_port = (PTP::Net::Port) p->GetPort(); m_ports.Insert(p); p->m_thread.Start(PortThread, p); return p;}/* * Trut::PortThread: Handle server connection. * @context: Port context. * Returns: NULL. */void *Trut::PortThread(void *context){ Port *port = (Port*) context; for (;;) { PTP::Net::Connection *conn = port->m_conn->Accept(1); if (!conn) continue; char url[256]; sprintf(url, "%lu.%lu.%lu.%lu:%u", (conn->GetIp() >> 24) & 0xff, (conn->GetIp() >> 16) & 0xff, (conn->GetIp() >> 8) & 0xff, (conn->GetIp() & 0xff), conn->GetPort()); Host *host = new Host; host->m_trut = port->m_trut; host->m_conn = conn; host->m_url = strdup(url); if (host->m_conn->GetType() == PTP::Net::Connection::HTTP) { PutContext ctx; ctx.m_host = host; ctx.m_resp = new BYTE[HEADER_SIZE]; ctx.m_id = NULL; if (host->m_trut->PutAuth(conn, &ctx.m_id, ctx.m_resp) == 0) host->m_thread.Start(PutThread, &ctx); else { delete [] ctx.m_resp; delete host; } } else { host->m_trut->m_hosts.Insert(host); if (host->m_trut->m_open) (*host->m_trut->m_open)(host, &host->m_context); host->m_thread.Start(HostThread, host); } } port->m_trut->m_ports.Remove(port); delete port; PTP::Thread::Exit(0); return NULL;}/* * Trut::PutThread: Handle file transmit. * @context: Transmit context. * Returns: NULL. */void *Trut::PutThread(void *context){ PutContext *ctx = (PutContext*) context; Host *host = ctx->m_host; PTP::Collection::Entry *entry = NULL; PTP::Key *key = NULL; int size = 0; char *hdr = new char[HEADER_SIZE]; if (!host->m_conn->ReadHttpHdr(hdr, HEADER_SIZE)) { delete [] hdr; delete [] ctx->m_resp; delete host; PTP::Thread::Exit(0); return NULL; } if (STRNCMP_CONST(hdr, "GET /get/") == 0) { char *start = hdr + STRLEN_CONST("GET /get/"); unsigned long ref = strtoul(start, NULL, 10); entry = host->m_trut->m_collect.Find(ref); } else if (STRNCMP_CONST(hdr, "GET /gets/") == 0) { char *start = hdr + STRLEN_CONST("GET /gets/"); char *end = strchr(start, '/'); *end = '\0'; Group *group = host->m_trut->FindGroup(start); if (!group || !group->m_key) goto fail; key = group->m_key; start = end + 1; unsigned long ref = strtoul(start, NULL, 16); entry = host->m_trut->m_collect.Find(ref); start = (char*) entry->GetName(); end = strrchr(start, '/'); int iskey = (strncmp(start, "/secure/", 8) == 0 && end && strcmp(end + 1, "key") == 0); if (iskey) key = NULL; if (iskey && group->m_accept && ctx->m_id && (*group->m_accept)(group, ctx->m_id->GetName(), group->m_context) == 0) goto fail; } else goto fail; if (!entry) goto fail; // get data size size = entry->GetSize(); if (ctx->m_id) { size = PTP::Identity::CIPHERTEXT_SIZE; } else if (key) { if (entry->GetPath()) { TransferContext ctx(fopen(entry->GetPath(), "rb")); if (!ctx.fp) goto fail; size = key->Encrypt(PutRead, NULL, &ctx); fclose(ctx.fp); } else { size = key->Encrypt(entry->GetData(), entry->GetSize(), NULL); } } // send header sprintf(hdr, "HTTP 200\r\n" "Content-type:application/binary\r\n" "Content-length:%d\r\n" "%s\r\n", size, ctx->m_resp); host->m_conn->WriteAll((BYTE*) hdr, strlen(hdr)); // send data if (entry->GetPath()) { TransferContext ctx(fopen(entry->GetPath(), "rb"), host->m_conn); if (!ctx.fp) goto fail; if (key) key->Encrypt(PutRead, PutWrite, &ctx); else PTP::Key::Transfer(PutRead, PutWrite, &ctx); fclose(ctx.fp); } else { if (ctx->m_id) { BYTE buffer[PTP::Identity::CIPHERTEXT_SIZE]; ctx->m_id->Encrypt(entry->GetData(), entry->GetSize(), buffer); host->m_conn->WriteAll(buffer, sizeof(buffer)); } else if (key) { BYTE *buffer = new BYTE[size]; key->Encrypt(entry->GetData(), entry->GetSize(), buffer); host->m_conn->WriteAll(buffer, size); delete [] buffer; } else { host->m_conn->WriteAll(entry->GetData(), entry->GetSize()); } } delete [] hdr; delete [] ctx->m_resp; delete host; PTP::Thread::Exit(0); return NULL; fail: sprintf(hdr, "HTTP 404\r\n" "Content-type: text/html\r\n\r\n" "<B>404 NOT FOUND</B>\n"); host->m_conn->WriteAll((BYTE*) hdr, strlen(hdr)); delete [] hdr; delete [] ctx->m_resp; delete host; PTP::Thread::Exit(0); return NULL;}/** * Trut::RemovePort: Destroy a server port. * @port: Port connection. */voidTrut::RemovePort(Port *port){ m_ports.Remove(port); port->m_thread.Kill(); delete port;}/** * Trut::RemoveAllPorts: Destroy all server ports. */voidTrut::RemoveAllPorts(){ Port *port; m_ports.Lock(); PTP_LIST_FOREACH(Port, port, &m_ports) { m_ports.Remove(port, 0); port->m_thread.Kill(); delete port; } m_ports.Unlock();}/** * Trut::AddHost: Create a new Gnutella connection. * @url: Destination URL. * Returns: Client connection or NULL on error. */Trut::Host *Trut::AddHost(const char *url){ PTP::Net::Port port; PTP::Net::Ip ip = PTP::Net::Lookup(url, &port, PORT_DEFAULT); if (!ip) return NULL; Host *host = new Host; host->m_trut = this; host->m_url = strdup(url); host->m_conn = new PTP::Net::Connection(PTP::Net::Connection::RAW, ip, port); host->SendGnutella((BYTE*) GNUTELLA_CONNECT_REQUEST); BYTE *pkt = host->ReceiveGnutella(); int ok = (pkt && STRNCMP_CONST((char*) pkt, GNUTELLA_CONNECT_RESPONSE) == 0); delete [] pkt; if (!ok) { delete host; return NULL; } m_hosts.Insert(host); if (m_open) (*m_open)(host, &host->m_context); host->m_thread.Start(HostThread, host); return host;}/* * Trut::HostThread: Handle client connection. * @context: Client context. * Returns: NULL. */void *Trut::HostThread(void *context){ Host *host = (Host*) context; for (;;) { Gnutella::Packet *pkt = (Gnutella::Packet*) host->ReceiveGnutella(); if (!pkt) break; if (STRNCMP_CONST((char*) pkt, GNUTELLA_CONNECT_REQUEST) == 0) { host->SendGnutella((BYTE*) GNUTELLA_CONNECT_RESPONSE); } else { switch (pkt->type) { case Gnutella::SEARCH_RESPONSE: host->m_trut->HandleSearchResponse( host, (Gnutella::SearchResp*) pkt); break; case Gnutella::SEARCH_REQUEST: host->m_trut->HandleSearchRequest( host, (Gnutella::SearchRqst*) pkt); break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -