| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429 |
- /*
- * PROGRAM: JRD Remote Interface/Server
- * MODULE: inet.cpp
- * DESCRIPTION: TCP/UCP/IP Communications module.
- *
- * The contents of this file are subject to the Interbase Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy
- * of the License at http://www.Inprise.com/IPL.html
- *
- * Software distributed under the License is distributed on an
- * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
- * or implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * The Original Code was created by Inprise Corporation
- * and its predecessors. Portions created by Inprise Corporation are
- * Copyright (C) Inprise Corporation.
- *
- * All Rights Reserved.
- * Contributor(s): ______________________________________.
- *
- * 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "EPSON" port
- * 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "XENIX" port
- * 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "IMP" port
- * 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "NCR3000" port
- *
- * 2002-02-23 Sean Leyne - Code Cleanup, removed old M88K and NCR3000 port
- *
- * 2002.10.27 Sean Leyne - Code Cleanup, removed obsolete "UNIXWARE" port
- * 2002.10.27 Sean Leyne - Code Cleanup, removed obsolete "Ultrix/MIPS" port
- *
- * 2002.10.28 Sean Leyne - Completed removal of obsolete "DGUX" port
- *
- * 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
- *
- * 2002.10.30 Sean Leyne - Removed support for obsolete "PC_PLATFORM" define
- * 2002.10.30 Sean Leyne - Code Cleanup, removed obsolete "SUN3_3" port
- * 2005.04.01 Konstantin Kuznetsov - allow setting NoNagle option in Classic
- *
- */
- #include "firebird.h"
- #include <stdio.h>
- #ifdef HAVE_SYS_TYPES_H
- #include <sys/types.h>
- #endif
- #include <errno.h>
- #include <string.h>
- #include <stdlib.h>
- #include "../common/file_params.h"
- #include <stdarg.h>
- #include "../common/classes/timestamp.h"
- #include "../common/classes/init.h"
- #include "../common/ThreadStart.h"
- #include "../common/os/os_utils.h"
- #ifdef HAVE_PWD_H
- #include <pwd.h>
- #endif
- #ifdef HAVE_SYS_PARAM_H
- #include <sys/param.h>
- #endif
- #ifdef HAVE_GRP_H
- #include <grp.h>
- #endif
- #ifdef HAVE_SYS_SOCKET_H
- #include <sys/socket.h> // for socket()
- #endif
- #ifdef HAVE_UNISTD_H
- #include <unistd.h>
- #endif
- #ifdef WIN_NT
- #define FD_SETSIZE 2048
- #endif
- #ifndef WIN_NT
- #include <netinet/tcp.h>
- #include <netinet/in.h>
- #include <netdb.h>
- #include <arpa/inet.h>
- #include <sys/wait.h>
- #if defined(HAVE_POLL_H)
- #include <poll.h>
- #elif defined(HAVE_SYS_SELECT_H)
- #include <sys/select.h>
- #endif
- #endif // !WIN_NT
- const int INET_RETRY_CALL = 5;
- #include "../remote/remote.h"
- #include "../remote/SockAddr.h"
- #include "ibase.h"
- #include "../remote/inet_proto.h"
- #include "../remote/proto_proto.h"
- #include "../remote/remot_proto.h"
- #include "../yvalve/gds_proto.h"
- #include "../common/isc_proto.h"
- #include "../common/isc_f_proto.h"
- #include "../common/os/isc_i_proto.h"
- #include "../common/config/config.h"
- #include "../common/utils_proto.h"
- #include "../common/classes/ClumpletWriter.h"
- // Please review. Maybe not needed. See H_ERRNO in common.h.
- #if defined HPUX
- extern int h_errno;
- #endif
- using namespace Firebird;
- #ifdef WIN_NT
- #include <fcntl.h>
- #include <process.h>
- #include <signal.h>
- #include "../utilities/install/install_nt.h"
- #include <mstcpip.h>
- #ifndef SIO_LOOPBACK_FAST_PATH
- #define SIO_LOOPBACK_FAST_PATH _WSAIOW(IOC_VENDOR,16)
- #endif
- #define INET_RETRY_ERRNO WSAEINPROGRESS
- #define INET_ADDR_IN_USE WSAEADDRINUSE
- #define sleep(seconds) Sleep ((seconds) * 1000)
- const int NOTASOCKET = WSAENOTSOCK;
- #else // WIN_NT
- #ifndef INET_ADDR_IN_USE
- #define INET_ADDR_IN_USE EADDRINUSE
- #endif
- #ifndef INET_RETRY_ERRNO
- #define INET_RETRY_ERRNO TRY_AGAIN
- #endif
- const int NOTASOCKET = EBADF;
- #endif // WIN_NT
- static void SOCLOSE(SOCKET& socket)
- {
- SOCKET s = socket;
- if (s != INVALID_SOCKET)
- {
- socket = INVALID_SOCKET;
- #ifdef WIN_NT
- closesocket(s);
- #else
- close(s);
- #endif
- }
- }
- #ifndef ENOBUFS
- #define ENOBUFS 0
- #endif
- #ifndef FB_SEND_FLAGS
- #define FB_SEND_FLAGS 0
- #endif
- #ifndef FB_SETOPT_FLAGS
- #define FB_SETOPT_FLAGS 0
- #endif
- //
- //#define DEBUG 1
- //
- #ifdef DEBUG
- #ifdef HAVE_SYS_TIMEB_H
- # include <sys/timeb.h>
- #endif
- const int TRACE_packets = 1 << 0; // bit 0
- const int TRACE_operations = 1 << 1; // bit 1
- const int TRACE_summary = 1 << 2; // bit 2
- static int INET_trace = TRACE_summary | TRACE_packets | TRACE_operations;
- static time_t INET_start_time = 0;
- SLONG INET_force_error = -1; // simulate a network error
- static ULONG INET_count_send = 0;
- static ULONG INET_count_recv = 0;
- static ULONG INET_bytes_send = 0;
- static ULONG INET_bytes_recv = 0;
- static ULONG inet_debug_timer()
- {
- /**************************************
- *
- * i n e t _ d e b u g _ t i m e r
- *
- **************************************
- *
- * Functional description
- * Utility function used in DEBUG mode only to put a timestamp
- * since start of connection on each debug output message.
- *
- * This has been implemented and tested on SOLARIS, and may
- * need tweeking on any other platform where DEBUG is needed.
- *
- **************************************/
- #ifdef HAVE_GETTIMEOFDAY
- struct timeval tv;
- GETTIMEOFDAY(&tv);
- return (tv.tv_sec * 1000 + tv.tv_usec - INET_start_time);
- #else
- struct timeb now;
- ftime(&now);
- return (now.time * 1000 + now.millitm - INET_start_time);
- #endif // HAVE_GETTIMEOFDAY
- }
- #endif // DEBUG
- const ULONG MAX_DATA_LW = 1448; // Low Water mark
- const ULONG MAX_DATA_HW = 32768; // High Water mark
- const ULONG DEF_MAX_DATA = 8192;
- //const int MAXHOSTLEN = 64;
- const int SELECT_TIMEOUT = 60; // Dispatch thread select timeout (sec)
- class Select
- {
- #ifdef HAVE_POLL
- private:
- static const int SEL_INIT_EVENTS = POLLIN;
- static const int SEL_CHECK_MASK = POLLIN;
- pollfd* getPollFd(int n)
- {
- FB_SIZE_T pos;
- if (slct_poll.find(n, pos))
- return &slct_poll[pos];
- return nullptr;
- }
- #endif
- public:
- #ifdef HAVE_POLL
- Select()
- : slct_time(0), slct_count(0), slct_poll(*getDefaultMemoryPool()),
- slct_ready(*getDefaultMemoryPool())
- { }
- explicit Select(Firebird::MemoryPool& pool)
- : slct_time(0), slct_count(0), slct_poll(pool), slct_ready(pool)
- { }
- #else
- Select()
- : slct_time(0), slct_count(0), slct_width(0)
- {
- memset(&slct_fdset, 0, sizeof slct_fdset);
- }
- explicit Select(Firebird::MemoryPool& /*pool*/)
- : slct_time(0), slct_count(0), slct_width(0)
- {
- memset(&slct_fdset, 0, sizeof slct_fdset);
- }
- #endif
- enum HandleState {SEL_BAD, SEL_DISCONNECTED, SEL_NO_DATA, SEL_READY};
- // set first port to check for readyness
- void checkStart(RemPortPtr& port)
- {
- slct_main = port;
- slct_port = port;
- #ifdef WIRE_COMPRESS_SUPPORT
- slct_zport = nullptr;
- #endif
- }
- // get port to check for readyness
- // assume port_mutex is locked
- HandleState checkNext(RemPortPtr& port)
- {
- #ifdef WIRE_COMPRESS_SUPPORT
- if (slct_zport)
- {
- if (slct_zport->port_z_data &&
- (slct_zport->port_state != rem_port::DISCONNECTED))
- {
- port = slct_zport;
- slct_zport = nullptr; // Will be set again by select_multi() if needed
- return SEL_READY;
- }
- slct_zport = nullptr;
- }
- #endif
- if (slct_port && slct_port->port_state == rem_port::DISCONNECTED)
- {
- // restart from main port
- slct_port = nullptr;
- if (slct_main && slct_main->port_state == rem_port::DISCONNECTED)
- slct_main = nullptr;
- slct_port = slct_main;
- }
- port = slct_port;
- if (!slct_port)
- return SEL_NO_DATA;
- #ifdef WIRE_COMPRESS_SUPPORT
- if (slct_port->port_z_data)
- return SEL_READY;
- #endif
- slct_port = slct_port->port_next;
- return ok(port);
- }
- void setZDataPort(RemPortPtr& port)
- {
- #ifdef WIRE_COMPRESS_SUPPORT
- slct_zport = port;
- #endif
- }
- HandleState ok(const rem_port* port)
- {
- #ifdef WIRE_COMPRESS_SUPPORT
- if (port->port_z_data)
- return SEL_READY;
- #endif
- SOCKET n = port->port_handle;
- #if defined(WIN_NT)
- if (FD_ISSET(n, &slct_fdset))
- {
- unset(n);
- return SEL_READY;
- }
- return SEL_NO_DATA;
- #elif defined(HAVE_POLL)
- pollfd* pf = nullptr;
- FB_SIZE_T pos;
- if (slct_ready.find(n, pos))
- pf = slct_ready[pos];
- if (pf)
- {
- HandleState ret = pf->events & SEL_CHECK_MASK ? SEL_READY : SEL_NO_DATA;
- pf->events = 0; // unset
- return ret;
- }
- return n < 0 ? (port->port_flags & PORT_disconnect ? SEL_DISCONNECTED : SEL_BAD) : SEL_NO_DATA;
- #else
- if (n < 0 || n >= FD_SETSIZE)
- return port->port_flags & PORT_disconnect ? SEL_DISCONNECTED : SEL_BAD;
- if (n < slct_width && FD_ISSET(n, &slct_fdset))
- {
- unset(n);
- return SEL_READY;
- }
- return SEL_NO_DATA;
- #endif
- }
- void unset(SOCKET handle)
- {
- #if defined(HAVE_POLL)
- pollfd* pf = getPollFd(handle);
- if (pf)
- {
- pf->events = 0;
- }
- #else
- FD_CLR(handle, &slct_fdset);
- --slct_count;
- #endif
- }
- void set(SOCKET handle)
- {
- #ifdef HAVE_POLL
- FB_SIZE_T pos;
- if (slct_poll.find(handle, pos))
- {
- slct_poll[pos].events = SEL_INIT_EVENTS;
- }
- else
- {
- pollfd f;
- f.fd = handle;
- f.events = SEL_INIT_EVENTS;
- slct_poll.insert(pos, f);
- }
- #else
- FD_SET(handle, &slct_fdset);
- #ifdef WIN_NT
- ++slct_width;
- #else
- slct_width = MAX(slct_width, handle + 1);
- #endif // WIN_NT
- #endif // HAVE_POLL
- }
- void clear()
- {
- slct_count = 0;
- #if defined(HAVE_POLL)
- slct_poll.clear();
- #else
- slct_width = 0;
- FD_ZERO(&slct_fdset);
- #endif
- slct_main = nullptr;
- slct_port = nullptr;
- #ifdef WIRE_COMPRESS_SUPPORT
- slct_zport = nullptr;
- #endif
- }
- void select(timeval* timeout)
- {
- #ifdef HAVE_POLL
- slct_ready.clear();
- bool hasRequest = false;
- pollfd* const end = slct_poll.end();
- for (pollfd* pf = slct_poll.begin(); pf < end; ++pf)
- {
- pf->revents = pf->events;
- if (pf->events & SEL_CHECK_MASK)
- hasRequest = true;
- }
- if (!hasRequest)
- {
- errno = NOTASOCKET;
- slct_count = -1;
- return;
- }
- int milliseconds = timeout ? timeout->tv_sec * 1000 + timeout->tv_usec / 1000 : -1;
- slct_count = ::poll(slct_poll.begin(), slct_poll.getCount(), milliseconds);
- if (slct_count >= 0) // in case of error return revents may contain something bad
- {
- for (pollfd* pf = slct_poll.begin(); pf < end; ++pf)
- {
- pf->events = pf->revents;
- if (pf->revents & SEL_CHECK_MASK)
- slct_ready.add(pf);
- }
- }
- #else
- #ifdef WIN_NT
- slct_count = ::select(FD_SETSIZE, &slct_fdset, NULL, NULL, timeout);
- #else
- slct_count = ::select(slct_width, &slct_fdset, NULL, NULL, timeout);
- #endif // WIN_NT
- #endif // HAVE_POLL
- }
- int getCount()
- {
- return slct_count;
- }
- time_t slct_time;
- private:
- int slct_count;
- #ifdef HAVE_POLL
- class PollToFD
- {
- public:
- static int generate(const pollfd* p) { return p->fd; };
- static int generate(const pollfd& p) { return p.fd; };
- };
- SortedArray<pollfd, InlineStorage<pollfd, 8>, int, PollToFD> slct_poll;
- SortedArray<pollfd*, InlineStorage<pollfd*, 8>, int, PollToFD> slct_ready;
- #else
- int slct_width;
- fd_set slct_fdset;
- #endif
- RemPortPtr slct_main; // first port to check for readyness
- RemPortPtr slct_port; // next port to check for readyness
- #ifdef WIRE_COMPRESS_SUPPORT
- RemPortPtr slct_zport; // port with some compressed data remaining in the buffer
- #endif
- };
- static bool accept_connection(rem_port*, const P_CNCT*);
- #ifdef HAVE_SETITIMER
- static void alarm_handler(int);
- #endif
- static rem_port* alloc_port(rem_port*, const USHORT = 0);
- static rem_port* aux_connect(rem_port*, PACKET*);
- static void abort_aux_connection(rem_port*);
- static rem_port* aux_request(rem_port*, PACKET*);
- #if !defined(WIN_NT)
- static THREAD_ENTRY_DECLARE waitThread(THREAD_ENTRY_PARAM);
- static GlobalPtr<Mutex> waitThreadMutex;
- static unsigned int procCount = 0;
- #endif // WIN_NT
- static void disconnect(rem_port*);
- static void force_close(rem_port*);
- static int cleanup_ports(const int, const int, void*);
- #ifdef NO_FORK
- static int fork();
- #endif
- typedef Array<SOCKET> SocketsArray;
- #ifdef WIN_NT
- static int wsaExitHandler(const int, const int, void*);
- static int fork(SOCKET, USHORT);
- static THREAD_ENTRY_DECLARE forkThread(THREAD_ENTRY_PARAM);
- static GlobalPtr<Mutex> forkMutex;
- static HANDLE forkEvent = INVALID_HANDLE_VALUE;
- static bool forkThreadStarted = false;
- static SocketsArray* forkSockets;
- #endif
- static void get_peer_info(rem_port*);
- static void inet_gen_error(bool, rem_port*, const Arg::StatusVector& v);
- static void inet_error(bool, rem_port*, const TEXT*, ISC_STATUS, int);
- static bool inet_read(RemoteXdr*);
- static rem_port* inet_try_connect( PACKET*,
- Rdb*,
- const PathName&,
- const TEXT*,
- ClumpletReader&,
- RefPtr<const Config>*,
- const PathName*,
- int);
- static bool inet_write(RemoteXdr*);
- static rem_port* listener_socket(rem_port* port, USHORT flag, const addrinfo* pai);
- #ifdef DEBUG
- static void packet_print(const TEXT*, const UCHAR*, int, ULONG);
- #endif
- static bool packet_receive(rem_port*, UCHAR*, SSHORT, SSHORT*);
- static bool packet_receive2(rem_port*, UCHAR*, SSHORT, SSHORT*);
- static bool packet_send(rem_port*, const SCHAR*, SSHORT);
- static rem_port* receive(rem_port*, PACKET *);
- static rem_port* select_accept(rem_port*);
- static void select_port(rem_port*, Select*, RemPortPtr&);
- static bool select_multi(rem_port*, UCHAR* buffer, SSHORT bufsize, SSHORT* length, RemPortPtr&);
- static bool select_wait(rem_port*, Select*);
- static int send_full(rem_port*, PACKET *);
- static int send_partial(rem_port*, PACKET *);
- static RemoteXdr* xdrinet_create(rem_port*, UCHAR *, USHORT, enum xdr_op);
- static bool setNoNagleOption(rem_port*);
- static bool setKeepAlive(SOCKET);
- static FPTR_INT tryStopMainThread = 0;
- struct InetXdr : public RemoteXdr
- {
- virtual bool_t x_getbytes(SCHAR *, unsigned); // get some bytes from "
- virtual bool_t x_putbytes(const SCHAR*, unsigned); // put some bytes to "
- };
- #define MAXCLIENTS NOFILE - 10
- // Select uses bit masks of file descriptors in longs.
- #ifndef NBBY
- #define NBBY 8
- #endif
- #ifndef NFDBITS
- #if !defined(WIN_NT)
- #define NFDBITS (sizeof(SLONG) * NBBY)
- #define FD_SET(n, p) ((p)->fds_bits[(n) / NFDBITS] |= (1 << ((n) % NFDBITS)))
- #define FD_CLR(n, p) ((p)->fds_bits[(n) / NFDBITS] &= ~(1 << ((n) % NFDBITS)))
- #define FD_ISSET(n, p) ((p)->fds_bits[(n) / NFDBITS] & (1 << ((n) % NFDBITS)))
- #define FD_ZERO(p) memset(p, 0, sizeof(*(p)))
- #endif
- #endif
- #ifdef WIN_NT
- #define INTERRUPT_ERROR(x) (SYSCALL_INTERRUPTED(x) || (x) == WSAEINTR)
- #else
- #define INTERRUPT_ERROR(x) (SYSCALL_INTERRUPTED(x))
- #endif
- ULONG INET_remote_buffer;
- static GlobalPtr<Mutex> init_mutex;
- static volatile bool INET_initialized = false;
- static volatile bool INET_shutting_down = false;
- static Firebird::GlobalPtr<Select> INET_select;
- static rem_port* inet_async_receive = NULL;
- static GlobalPtr<Mutex> port_mutex;
- static GlobalPtr<PortsCleanup> inet_ports;
- static GlobalPtr<SocketsArray> ports_to_close;
- rem_port* INET_analyze(ClntAuthBlock* cBlock,
- const PathName& file_name,
- const TEXT* node_name,
- bool uv_flag,
- ClumpletReader &dpb,
- RefPtr<const Config>* config,
- const PathName* ref_db_name,
- Firebird::ICryptKeyCallback* cryptCb,
- int af)
- {
- /**************************************
- *
- * I N E T _ a n a l y z e
- *
- **************************************
- *
- * Functional description
- * File_name is on node_name.
- * Establish an external connection to node_name.
- *
- * If a connection is established, return a port block, otherwise
- * return NULL.
- *
- * If the "uv_flag" is non-zero, user verification also takes place.
- *
- **************************************/
- // We need to establish a connection to a remote server. Allocate the necessary
- // blocks and get ready to go.
- Rdb* rdb = FB_NEW Rdb;
- PACKET* packet = &rdb->rdb_packet;
- // Pick up some user identification information
- ClumpletWriter user_id(ClumpletReader::UnTagged, 64000);
- if (cBlock)
- {
- cBlock->extractDataFromPluginTo(user_id);
- }
- string buffer;
- int eff_gid;
- int eff_uid;
- ISC_get_user(&buffer, &eff_uid, &eff_gid);
- #ifdef WIN_NT
- // XNET lowercases user names (as it's always case-insensitive in Windows),
- // so let's be consistent and use the same trick for INET as well
- buffer.lower();
- #endif
- ISC_systemToUtf8(buffer);
- user_id.insertString(CNCT_user, buffer);
- ISC_get_host(buffer);
- buffer.lower();
- ISC_systemToUtf8(buffer);
- user_id.insertString(CNCT_host, buffer);
- if ((eff_uid == -1) || uv_flag) {
- user_id.insertTag(CNCT_user_verification);
- }
- else
- {
- // Communicate group id info to server, as user maybe running under group
- // id other than default specified in /etc/passwd.
- eff_gid = htonl(eff_gid);
- user_id.insertBytes(CNCT_group, reinterpret_cast<UCHAR*>(&eff_gid), sizeof(eff_gid));
- }
- // Should compression be tried?
- bool compression = config && (*config)->getWireCompression();
- // Establish connection to server
- // If we want user verification, we can't speak anything less than version 7
- P_CNCT* cnct = &packet->p_cnct;
- cnct->p_cnct_user_id.cstr_length = (ULONG) user_id.getBufferLength();
- cnct->p_cnct_user_id.cstr_address = user_id.getBuffer();
- static const p_cnct::p_cnct_repeat protocols_to_try[] =
- {
- REMOTE_PROTOCOL(PROTOCOL_VERSION10, ptype_lazy_send, 1),
- REMOTE_PROTOCOL(PROTOCOL_VERSION11, ptype_lazy_send, 2),
- REMOTE_PROTOCOL(PROTOCOL_VERSION12, ptype_lazy_send, 3),
- REMOTE_PROTOCOL(PROTOCOL_VERSION13, ptype_lazy_send, 4),
- REMOTE_PROTOCOL(PROTOCOL_VERSION14, ptype_lazy_send, 5),
- REMOTE_PROTOCOL(PROTOCOL_VERSION15, ptype_lazy_send, 6),
- REMOTE_PROTOCOL(PROTOCOL_VERSION16, ptype_lazy_send, 7),
- REMOTE_PROTOCOL(PROTOCOL_VERSION17, ptype_lazy_send, 8),
- REMOTE_PROTOCOL(PROTOCOL_VERSION18, ptype_lazy_send, 9),
- REMOTE_PROTOCOL(PROTOCOL_VERSION19, ptype_lazy_send, 10)
- };
- fb_assert(FB_NELEM(protocols_to_try) <= FB_NELEM(cnct->p_cnct_versions));
- cnct->p_cnct_count = FB_NELEM(protocols_to_try);
- for (size_t i = 0; i < cnct->p_cnct_count; i++) {
- cnct->p_cnct_versions[i] = protocols_to_try[i];
- if (compression && cnct->p_cnct_versions[i].p_cnct_version >= PROTOCOL_VERSION13 &&
- rem_port::checkCompression())
- {
- cnct->p_cnct_versions[i].p_cnct_max_type |= pflag_compress;
- }
- }
- rem_port* port = inet_try_connect(packet, rdb, file_name, node_name, dpb, config, ref_db_name, af);
- P_ACPT* accept;
- for (;;)
- {
- accept = NULL;
- switch (packet->p_operation)
- {
- case op_accept_data:
- case op_cond_accept:
- accept = &packet->p_acpd;
- if (cBlock)
- {
- cBlock->storeDataForPlugin(packet->p_acpd.p_acpt_data.cstr_length,
- packet->p_acpd.p_acpt_data.cstr_address);
- cBlock->authComplete = packet->p_acpd.p_acpt_authenticated;
- port->addServerKeys(&packet->p_acpd.p_acpt_keys);
- cBlock->resetClnt(&packet->p_acpd.p_acpt_keys);
- }
- break;
- case op_accept:
- if (cBlock)
- {
- cBlock->resetClnt();
- }
- accept = &packet->p_acpt;
- break;
- case op_crypt_key_callback:
- try
- {
- UCharBuffer buf;
- P_CRYPT_CALLBACK* cc = &packet->p_cc;
- if (cryptCb)
- {
- if (cc->p_cc_reply <= 0)
- {
- cc->p_cc_reply = 1;
- }
- UCHAR* reply = buf.getBuffer(cc->p_cc_reply);
- unsigned l = cryptCb->callback(cc->p_cc_data.cstr_length,
- cc->p_cc_data.cstr_address, cc->p_cc_reply, reply);
- REMOTE_free_packet(port, packet, true);
- cc->p_cc_data.cstr_length = l;
- cc->p_cc_data.cstr_address = reply;
- }
- else
- {
- REMOTE_free_packet(port, packet, true);
- cc->p_cc_data.cstr_length = 0;
- }
- packet->p_operation = op_crypt_key_callback;
- cc->p_cc_reply = 0;
- port->send(packet);
- port->receive(packet);
- continue;
- }
- catch (const Exception&)
- {
- disconnect(port);
- delete rdb;
- throw;
- }
- case op_response:
- try
- {
- LocalStatus warning; // Ignore connect warnings for a while
- CheckStatusWrapper statusWrapper(&warning);
- REMOTE_check_response(&statusWrapper, rdb, packet, false);
- }
- catch (const Exception&)
- {
- disconnect(port);
- delete rdb;
- throw;
- }
- // fall through - response is not a required accept
- default:
- disconnect(port);
- delete rdb;
- Arg::Gds(isc_connect_reject).raise();
- break;
- }
- break; // Always leave for() loop here
- }
- fb_assert(accept);
- fb_assert(port);
- port->port_protocol = accept->p_acpt_version;
- // once we've decided on a protocol, concatenate the version
- // string to reflect it...
- string temp;
- temp.printf("%s/P%d", port->port_version->str_data, port->port_protocol & FB_PROTOCOL_MASK);
- delete port->port_version;
- port->port_version = REMOTE_make_string(temp.c_str());
- if (accept->p_acpt_architecture == ARCHITECTURE) {
- port->port_flags |= PORT_symmetric;
- }
- bool compress = accept->p_acpt_type & pflag_compress;
- accept->p_acpt_type &= ptype_MASK;
- if (accept->p_acpt_type != ptype_out_of_band) {
- port->port_flags |= PORT_no_oob;
- }
- if (accept->p_acpt_type == ptype_lazy_send) {
- port->port_flags |= PORT_lazy;
- }
- if (compress)
- {
- port->initCompression();
- port->port_flags |= PORT_compressed;
- }
- return port;
- }
- rem_port* INET_connect(const TEXT* name,
- PACKET* packet,
- USHORT flag,
- ClumpletReader* dpb,
- RefPtr<const Config>* config,
- int af)
- {
- /**************************************
- *
- * I N E T _ c o n n e c t
- *
- **************************************
- *
- * Functional description
- * Establish half of a communication link. If a connect packet is given,
- * the connection is on behalf of a remote interface. Otherwise the connect
- * is for a server process.
- *
- **************************************/
- #ifdef DEBUG
- {
- if (INET_trace & TRACE_operations)
- {
- fprintf(stdout, "INET_connect\n");
- fflush(stdout);
- }
- INET_start_time = inet_debug_timer();
- // CVC: I don't see the point in replacing this with fb_utils::readenv().
- const char* p = getenv("INET_force_error");
- if (p != NULL) {
- INET_force_error = atoi(p);
- }
- }
- #endif
- rem_port* port = alloc_port(NULL);
- if (config)
- {
- port->port_config = *config;
- }
- REMOTE_get_timeout_params(port, dpb);
- string host;
- string protocol;
- if ((!name || !name[0]) && !packet)
- {
- name = port->getPortConfig()->getRemoteBindAddress();
- }
- if (name)
- {
- host = name;
- const FB_SIZE_T pos = host.find("/");
- if (pos != string::npos)
- {
- protocol = host.substr(pos + 1);
- host = host.substr(0, pos);
- }
- if (host.hasData() && host[0] == '[' && host[host.length() - 1] == ']')
- {
- // host name or address is in brackets, remove them
- host.erase(host.length() - 1);
- host.erase(0, 1);
- }
- }
- if (host.hasData())
- {
- delete port->port_connection;
- port->port_connection = nullptr;
- port->port_connection = REMOTE_make_string(host.c_str());
- }
- else if (packet)
- {
- host = port->port_host->str_data;
- }
- if (protocol.isEmpty())
- {
- const unsigned short port2 = port->getPortConfig()->getRemoteServicePort();
- if (port2) {
- protocol.printf("%hu", port2);
- }
- else {
- protocol = port->getPortConfig()->getRemoteServiceName();
- }
- }
- // Prepare hints
- const bool ipv6 = os_utils::isIPv6supported();
- struct addrinfo gai_hints;
- memset(&gai_hints, 0, sizeof(gai_hints));
- if (packet)
- gai_hints.ai_family = af;
- else
- gai_hints.ai_family = ((host.hasData() || !ipv6) ? AF_UNSPEC : AF_INET6);
- gai_hints.ai_socktype = SOCK_STREAM;
- #if !defined(WIN_NT) && !defined(__clang__)
- gai_hints.ai_protocol = SOL_TCP;
- #else
- gai_hints.ai_protocol = IPPROTO_TCP;
- #endif
- gai_hints.ai_flags =
- #ifndef ANDROID
- ((af == AF_UNSPEC) ? AI_V4MAPPED : 0) |
- #endif
- AI_ADDRCONFIG | (packet ? 0 : AI_PASSIVE);
- struct AutoAddrInfo
- {
- ~AutoAddrInfo()
- {
- if (ptr)
- freeaddrinfo(ptr);
- }
- addrinfo* ptr = nullptr;
- } gai_result;
- const char* host_str = (host.hasData() ? host.c_str() : NULL);
- bool retry_gai;
- int n;
- do
- {
- retry_gai = false;
- n = getaddrinfo(host_str, protocol.c_str(), &gai_hints, &gai_result.ptr);
- if ((n == EAI_FAMILY || (!host_str && n == EAI_NONAME)) &&
- (gai_hints.ai_family == AF_INET6) && (af != AF_INET6))
- {
- // May be on a system without IPv6 support, try IPv4
- gai_hints.ai_family = AF_UNSPEC;
- retry_gai = true;
- }
- if ((n == EAI_SERVICE) && (protocol == FB_SERVICE_NAME))
- {
- // Try hard-wired translation of "gds_db" to "3050"
- protocol.printf("%hu", FB_SERVICE_PORT);
- retry_gai = (protocol != FB_SERVICE_NAME);
- }
- } while (retry_gai);
- if (n)
- {
- gds__log("INET/INET_connect: getaddrinfo(%s,%s) failed: %s",
- host.c_str(), protocol.c_str(), gai_strerror(n));
- inet_gen_error(true, port, Arg::Gds(isc_net_lookup_err) << Arg::Gds(isc_host_unknown));
- }
- for (const addrinfo* pai = gai_result.ptr; pai; pai = pai->ai_next)
- {
- // Allocate a port block and initialize a socket for communications
- port->port_handle = os_utils::socket(pai->ai_family, pai->ai_socktype, pai->ai_protocol);
- if (port->port_handle == INVALID_SOCKET)
- {
- gds__log("socket: error creating socket (family %d, socktype %d, protocol %d",
- pai->ai_family, pai->ai_socktype, pai->ai_protocol);
- continue;
- }
- if (!packet) // server
- return listener_socket(port, flag, pai);
- // client
- if (!setKeepAlive(port->port_handle))
- gds__log("setsockopt: error setting SO_KEEPALIVE");
- if (!setNoNagleOption(port))
- gds__log("setsockopt: error setting TCP_NODELAY");
- else
- {
- n = connect(port->port_handle, pai->ai_addr, pai->ai_addrlen);
- if (n != -1)
- {
- port->port_peer_name = host;
- get_peer_info(port);
- if (send_full(port, packet))
- return port;
- }
- }
- SOCLOSE(port->port_handle);
- }
- // all attempts failed
- if (packet)
- inet_error(true, port, "connect", isc_net_connect_err, 0);
- else
- inet_error(true, port, "listen", isc_net_connect_listen_err, 0);
- return port;
- }
- static rem_port* listener_socket(rem_port* port, USHORT flag, const addrinfo* pai)
- {
- /**************************************
- *
- * l i s t e n e r _ s o c k e t
- *
- **************************************
- *
- * Functional description
- * Final part of server (listening) socket setup. Sets socket options,
- * binds the socket and calls listen().
- * For multi-client server (SuperServer or SuperClassic) return listener
- * port.
- * For classic server - accept incoming connections and fork worker
- * processes, return NULL at exit;
- * On error throw exception.
- *
- **************************************/
- int ipv6_v6only = port->getPortConfig()->getIPv6V6Only() ? 1 : 0;
- int n = setsockopt(port->port_handle, IPPROTO_IPV6, IPV6_V6ONLY,
- (SCHAR*) &ipv6_v6only, sizeof(ipv6_v6only));
- if (n == -1)
- gds__log("setsockopt: error setting IPV6_V6ONLY to %d", ipv6_v6only);
- if (flag & SRVR_multi_client)
- {
- struct linger lingerInfo;
- lingerInfo.l_onoff = 0;
- lingerInfo.l_linger = 0;
- #ifndef WIN_NT
- // dimitr: on Windows, lack of SO_REUSEADDR works the same way as it was specified on POSIX,
- // i.e. it allows binding to a port in a TIME_WAIT/FIN_WAIT state. If this option
- // is turned on explicitly, then a port can be re-bound regardless of its state,
- // e.g. while it's listening. This is surely not what we want.
- int optval = TRUE;
- n = setsockopt(port->port_handle, SOL_SOCKET, SO_REUSEADDR,
- (SCHAR*) &optval, sizeof(optval));
- if (n == -1)
- {
- inet_error(true, port, "setsockopt REUSE", isc_net_connect_listen_err, INET_ERRNO);
- }
- #endif
- // Get any values for SO_LINGER so that they can be reset during
- // disconnect. SO_LINGER should be set by default on the socket
- socklen_t optlen = sizeof(port->port_linger);
- n = getsockopt(port->port_handle, SOL_SOCKET, SO_LINGER,
- (SCHAR *) & port->port_linger, &optlen);
- if (n != 0) // getsockopt failed
- port->port_linger.l_onoff = 0;
- n = setsockopt(port->port_handle, SOL_SOCKET, SO_LINGER,
- (SCHAR *) & lingerInfo, sizeof(lingerInfo));
- if (n == -1)
- {
- inet_error(true, port, "setsockopt LINGER", isc_net_connect_listen_err, INET_ERRNO);
- }
- }
- else
- {
- if (! setKeepAlive(port->port_handle))
- {
- inet_error(true, port, "setsockopt SO_KEEPALIVE", isc_net_connect_listen_err, INET_ERRNO);
- }
- }
- // RS: In linux sockets inherit this option from listener. Previously CLASSIC had no its own listen socket
- // Now it's necessary to respect the option via listen socket.
- if (! setNoNagleOption(port))
- {
- inet_error(true, port, "setsockopt TCP_NODELAY", isc_net_connect_listen_err, INET_ERRNO);
- }
- // On Linux platform, when the server dies the system holds a port
- // for some time (we don't set SO_REUSEADDR for standalone server).
- int retry = -1;
- do
- {
- if (++retry)
- sleep(10);
- n = bind(port->port_handle, pai->ai_addr, pai->ai_addrlen);
- } while (n == -1 && INET_ERRNO == INET_ADDR_IN_USE && retry < INET_RETRY_CALL);
- if (n == -1)
- {
- inet_error(true, port, "bind", isc_net_connect_listen_err, INET_ERRNO);
- }
- n = listen(port->port_handle, SOMAXCONN);
- if (n == -1)
- {
- inet_error(false, port, "listen", isc_net_connect_listen_err, INET_ERRNO);
- }
- inet_ports->registerPort(port);
- if (flag & SRVR_multi_client)
- {
- // Prevent the generation of dummy keepalive packets on the connect port.
- port->port_dummy_packet_interval = 0;
- port->port_dummy_timeout = 0;
- port->port_server_flags |= (SRVR_server | SRVR_multi_client);
- return port;
- }
- while (true)
- {
- SOCKET s = os_utils::accept(port->port_handle, NULL, NULL);
- const int inetErrNo = INET_ERRNO;
- if (s == INVALID_SOCKET)
- {
- if (INET_shutting_down)
- return NULL;
- inet_error(true, port, "accept", isc_net_connect_err, inetErrNo);
- }
- #ifdef WIN_NT
- if (flag & SRVR_debug)
- #else
- if ((flag & SRVR_debug) || !fork())
- #endif
- {
- SOCLOSE(port->port_handle);
- port->port_handle = s;
- port->port_server_flags |= SRVR_server;
- port->port_flags |= PORT_server;
- return port;
- }
- #ifdef WIN_NT
- MutexLockGuard forkGuard(forkMutex, FB_FUNCTION);
- if (!forkThreadStarted)
- {
- forkThreadStarted = true;
- forkEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- forkSockets = FB_NEW SocketsArray(*getDefaultMemoryPool());
- Thread::start(forkThread, (void*) flag, THREAD_medium);
- }
- forkSockets->add(s);
- SetEvent(forkEvent);
- #else
- MutexLockGuard guard(waitThreadMutex, FB_FUNCTION);
- if (! procCount++) {
- Thread::start(waitThread, 0, THREAD_medium);
- }
- SOCLOSE(s);
- #endif
- }
- #ifdef WIN_NT
- MutexLockGuard forkGuard(forkMutex, FB_FUNCTION);
- if (forkThreadStarted)
- {
- SetEvent(forkEvent);
- CloseHandle(forkEvent);
- delete forkSockets;
- forkSockets = NULL;
- }
- #endif
- return NULL;
- }
- rem_port* INET_reconnect(SOCKET handle)
- {
- /**************************************
- *
- * I N E T _ r e c o n n e c t
- *
- **************************************
- *
- * Functional description
- * A communications link has been established by another
- * process. We have inheritted the handle. Set up
- * a port block.
- *
- **************************************/
- rem_port* const port = alloc_port(NULL);
- port->port_handle = handle;
- port->port_flags |= PORT_server;
- port->port_server_flags |= SRVR_server;
- if (! setKeepAlive(port->port_handle)) {
- gds__log("inet server err: setting KEEPALIVE socket option \n");
- }
- if (! setNoNagleOption(port)) {
- gds__log("inet server err: setting NODELAY socket option \n");
- }
- return port;
- }
- rem_port* INET_server(SOCKET sock)
- {
- /**************************************
- *
- * I N E T _ s e r v e r
- *
- **************************************
- *
- * Functional description
- * We have been spawned by a master server with a connection
- * established. Set up port block with the appropriate socket.
- *
- **************************************/
- rem_port* const port = alloc_port(NULL);
- port->port_flags |= PORT_server;
- port->port_server_flags |= SRVR_server;
- port->port_handle = sock;
- if (! setKeepAlive(port->port_handle)) {
- gds__log("inet server err: setting KEEPALIVE socket option \n");
- }
- if (! setNoNagleOption(port)) {
- gds__log("inet server err: setting NODELAY socket option \n");
- }
- return port;
- }
- static bool accept_connection(rem_port* port, const P_CNCT* cnct)
- {
- /**************************************
- *
- * a c c e p t _ c o n n e c t i o n
- *
- **************************************
- *
- * Functional description
- * Accept an incoming request for connection. This is purely a lower
- * level handshaking function, and does not constitute the server
- * response for protocol selection.
- *
- **************************************/
- // Default account to "guest" (in theory all packets contain a name)
- string user_name("guest"), host_name;
- // Pick up account and host name, if given
- ClumpletReader id(ClumpletReader::UnTagged,
- cnct->p_cnct_user_id.cstr_address,
- cnct->p_cnct_user_id.cstr_length);
- for (id.rewind(); !id.isEof(); id.moveNext())
- {
- switch (id.getClumpTag())
- {
- case CNCT_user:
- id.getString(user_name);
- break;
- case CNCT_host:
- id.getString(host_name);
- break;
- default:
- break;
- }
- }
- #ifndef WIN_NT
- { // scope
- // If the environment variable ISC_INET_SERVER_HOME is set,
- // change the home directory to the specified directory.
- // Note that this will overrule the normal setting of
- // the current directory to the effective user's home directory.
- // This feature was added primarily for testing via remote
- // loopback - but does seem to be of good general use, so
- // is activated for the release version.
- // 1995-February-27 David Schnepper
- PathName home;
- if (fb_utils::readenv("ISC_INET_SERVER_HOME", home))
- {
- if (chdir(home.c_str()))
- {
- gds__log("inet_server: unable to cd to %s errno %d\n", home.c_str(), INET_ERRNO);
- // We continue after the error
- }
- }
- } // end scope
- #endif // !WIN_NT
- // store user identity
- port->port_login = port->port_user_name = user_name;
- port->port_peer_name = host_name;
- get_peer_info(port);
- return true;
- }
- static rem_port* alloc_port(rem_port* const parent, const USHORT flags)
- {
- /**************************************
- *
- * a l l o c _ p o r t
- *
- **************************************
- *
- * Functional description
- * Allocate a port block, link it in to parent (if there is a parent),
- * and initialize input and output XDR streams.
- *
- **************************************/
- if (!INET_initialized)
- {
- MutexLockGuard guard(init_mutex, FB_FUNCTION);
- if (!INET_initialized)
- {
- #ifdef WIN_NT
- static WSADATA wsadata;
- const WORD version = MAKEWORD(2, 0);
- const int wsaError = WSAStartup(version, &wsadata);
- if (wsaError)
- {
- inet_error(false, parent, "WSAStartup", isc_net_init_error, wsaError);
- }
- fb_shutdown_callback(0, wsaExitHandler, fb_shut_finish, 0);
- #endif
- INET_remote_buffer = Config::getTcpRemoteBufferSize();
- if (INET_remote_buffer < MAX_DATA_LW || INET_remote_buffer > MAX_DATA_HW)
- {
- INET_remote_buffer = DEF_MAX_DATA;
- }
- #ifdef DEBUG
- gds__log(" Info: Remote Buffer Size set to %ld", INET_remote_buffer);
- #endif
- fb_shutdown_callback(0, cleanup_ports, fb_shut_postproviders, 0);
- INET_initialized = true;
- // This should go AFTER 'INET_initialized = true' to avoid recursion
- // That order of statements at the first glance appears to be possible cause of races:
- // Someone may pass the if (!INET_initialized) with INET_initialized == true,
- // but inet_async_receive still being NULL. Luckily it's not so awful actually.
- // Async receive is used only by network server and there is no way for it
- // to allocate secondary ports until completion of master port init.
- inet_async_receive = alloc_port(0);
- inet_async_receive->port_flags |= PORT_server;
- }
- }
- rem_port* const port = FB_NEW rem_port(rem_port::INET, INET_remote_buffer * 2);
- REMOTE_get_timeout_params(port, 0);
- TEXT buffer[BUFFER_SMALL];
- gethostname(buffer, sizeof(buffer));
- port->port_host = REMOTE_make_string(buffer);
- port->port_connection = REMOTE_make_string(buffer);
- SNPRINTF(buffer, FB_NELEM(buffer), "tcp (%s)", port->port_host->str_data);
- port->port_version = REMOTE_make_string(buffer);
- port->port_accept = accept_connection;
- port->port_disconnect = disconnect;
- port->port_force_close = force_close;
- port->port_receive_packet = receive;
- port->port_select_multi = select_multi;
- port->port_send_packet = send_full;
- port->port_send_partial = send_partial;
- port->port_connect = aux_connect;
- port->port_abort_aux_connection = abort_aux_connection;
- port->port_request = aux_request;
- port->port_buff_size = (USHORT) INET_remote_buffer;
- port->port_async_receive = inet_async_receive;
- port->port_flags |= flags;
- port->port_send = xdrinet_create(port,
- &port->port_buffer[REM_SEND_OFFSET(INET_remote_buffer)],
- (USHORT) INET_remote_buffer, XDR_ENCODE);
- port->port_receive = xdrinet_create(port,
- &port->port_buffer[REM_RECV_OFFSET(INET_remote_buffer)], 0, XDR_DECODE);
- if (parent && !(parent->port_server_flags & SRVR_thread_per_port))
- {
- MutexLockGuard guard(port_mutex, FB_FUNCTION);
- port->linkParent(parent);
- }
- return port;
- }
- static void abort_aux_connection(rem_port* port)
- {
- if (port->port_flags & PORT_connecting)
- {
- shutdown(port->port_channel, 2);
- SOCLOSE(port->port_channel);
- }
- }
- static rem_port* aux_connect(rem_port* port, PACKET* packet)
- {
- /**************************************
- *
- * a u x _ c o n n e c t
- *
- **************************************
- *
- * Functional description
- * Try to establish an alternative connection. Somebody has already
- * done a successfull connect request ("packet" contains the response).
- *
- **************************************/
- // If this is a server, we're got an auxiliary connection. Accept it
- if (port->port_server_flags)
- {
- struct timeval timeout;
- timeout.tv_sec = port->port_connect_timeout;
- timeout.tv_usec = 0;
- Select slct;
- slct.set(port->port_channel);
- int inetErrNo = 0;
- while (true)
- {
- slct.select(&timeout);
- const int count = slct.getCount();
- inetErrNo = INET_ERRNO;
- if (count != -1 || !INTERRUPT_ERROR(inetErrNo))
- {
- if (count == 1)
- break;
- const ISC_STATUS error_code =
- (count == 0) ? isc_net_event_connect_timeout : isc_net_event_connect_err;
- int savedError = inetErrNo;
- SOCLOSE(port->port_channel);
- inet_error(false, port, "select", error_code, savedError);
- }
- }
- if (port->port_channel == INVALID_SOCKET)
- return NULL;
- const SOCKET n = os_utils::accept(port->port_channel, NULL, NULL);
- inetErrNo = INET_ERRNO;
- if (n == INVALID_SOCKET)
- {
- int savedError = inetErrNo;
- SOCLOSE(port->port_channel);
- inet_error(false, port, "accept", isc_net_event_connect_err, savedError);
- }
- SOCLOSE(port->port_channel);
- port->port_handle = n;
- port->port_flags |= PORT_async;
- get_peer_info(port);
- return port;
- }
- rem_port* const new_port = alloc_port(port->port_parent,
- (port->port_flags & PORT_no_oob) | PORT_async);
- port->port_async = new_port;
- new_port->port_dummy_packet_interval = port->port_dummy_packet_interval;
- new_port->port_dummy_timeout = new_port->port_dummy_packet_interval;
- P_RESP* response = &packet->p_resp;
- // NJK - Determine address and port to use.
- //
- // The address returned by the server may be incorrect if it is behind a NAT box
- // so we must use the address that was used to connect the main socket, not the
- // address reported by the server.
- //
- // The port number reported by the server is used. For NAT support the port number
- // should be configured to be a fixed port number in the server configuration.
- SockAddr address;
- int status = address.getpeername(port->port_handle);
- if (status != 0)
- {
- int savedError = INET_ERRNO;
- port->auxAcceptError(packet);
- inet_error(false, port, "socket", isc_net_event_connect_err, savedError);
- }
- SockAddr resp_address(response->p_resp_data.cstr_address, response->p_resp_data.cstr_length);
- address.setPort(resp_address.port());
- // Set up new socket
- SOCKET n = os_utils::socket(address.family(), SOCK_STREAM, 0);
- if (n == INVALID_SOCKET)
- {
- int savedError = INET_ERRNO;
- port->auxAcceptError(packet);
- inet_error(false, port, "socket", isc_net_event_connect_err, savedError);
- }
- setKeepAlive(n);
- status = address.connect(n);
- if (status < 0)
- {
- int savedError = INET_ERRNO;
- SOCLOSE(n);
- port->auxAcceptError(packet);
- inet_error(false, port, "connect", isc_net_event_connect_err, savedError);
- }
- new_port->port_handle = n;
- new_port->port_peer_name = port->port_peer_name;
- get_peer_info(new_port);
- return new_port;
- }
- static rem_port* aux_request( rem_port* port, PACKET* packet)
- {
- /**************************************
- *
- * a u x _ r e q u e s t
- *
- **************************************
- *
- * Functional description
- * A remote interface has requested the server prepare an auxiliary
- * connection; the server calls aux_request to set up the connection.
- *
- **************************************/
- // listen on (local) address of the original socket
- SockAddr our_address;
- if (our_address.getsockname(port->port_handle) < 0)
- {
- gds__log("INET/aux_request: failed to get local address of the original socket");
- inet_error(false, port, "getsockname", isc_net_event_listen_err, INET_ERRNO);
- }
- unsigned short aux_port = port->getPortConfig()->getRemoteAuxPort();
- our_address.setPort(aux_port); // may be 0
- SOCKET n = os_utils::socket(our_address.family(), SOCK_STREAM, 0);
- if (n == INVALID_SOCKET)
- {
- inet_error(false, port, "socket", isc_net_event_listen_err, INET_ERRNO);
- }
- int optval;
- #ifndef WIN_NT
- // dimitr: on Windows, lack of SO_REUSEADDR works the same way as it was specified on POSIX,
- // i.e. it allows binding to a port in a TIME_WAIT/FIN_WAIT state. If this option
- // is turned on explicitly, then a port can be re-bound regardless of its state,
- // e.g. while it's listening. This is surely not what we want.
- optval = TRUE;
- if (setsockopt(n, SOL_SOCKET, SO_REUSEADDR, (SCHAR*) &optval, sizeof(optval)) < 0)
- {
- inet_error(false, port, "setsockopt REUSE", isc_net_event_listen_err, INET_ERRNO);
- }
- #endif
- optval = port->getPortConfig()->getIPv6V6Only() ? 1 : 0;
- // ignore failure, we already have it logged from the main listening port
- setsockopt(n, IPPROTO_IPV6, IPV6_V6ONLY, (SCHAR*) &optval, sizeof(optval));
- if (bind(n, our_address.ptr(), our_address.length()) < 0)
- {
- inet_error(false, port, "bind", isc_net_event_listen_err, INET_ERRNO);
- }
- if (our_address.getsockname(n) < 0)
- {
- inet_error(false, port, "getsockname", isc_net_event_listen_err, INET_ERRNO);
- }
- if (listen(n, 1) < 0)
- {
- inet_error(false, port, "listen", isc_net_event_listen_err, INET_ERRNO);
- }
- rem_port* const new_port = alloc_port(port->port_parent,
- (port->port_flags & PORT_no_oob) | PORT_async | PORT_connecting);
- port->port_async = new_port;
- new_port->port_dummy_packet_interval = port->port_dummy_packet_interval;
- new_port->port_dummy_timeout = new_port->port_dummy_packet_interval;
- new_port->port_server_flags = port->port_server_flags;
- new_port->port_channel = (int) n;
- P_RESP* response = &packet->p_resp;
- SockAddr port_address;
- if (port_address.getsockname(port->port_handle) < 0)
- inet_error(false, port, "getsockname", isc_net_event_listen_err, INET_ERRNO);
- port_address.setPort(our_address.port());
- // CORE-5902: MacOS has sockaddr struct layout different than one found in POSIX/Windows.
- // This prevent usage of events when client or server is MacOS but the other end is not.
- // Here we try to make this case work. However it's not bullet-proof for others platforms and architectures.
- // A proper solution would be to just send the port number in a protocol friendly way.
- bool macOsClient =
- port->port_client_arch == arch_darwin_ppc ||
- port->port_client_arch == arch_darwin_x64 ||
- port->port_client_arch == arch_darwin_ppc64;
- bool macOsServer =
- ARCHITECTURE == arch_darwin_ppc ||
- ARCHITECTURE == arch_darwin_x64 ||
- ARCHITECTURE == arch_darwin_ppc64;
- if (macOsServer && !macOsClient)
- port_address.convertFromMacOsToPosixWindows();
- else if (!macOsServer && macOsClient)
- port_address.convertFromPosixWindowsToMacOs();
- response->p_resp_data.cstr_length = (ULONG) port_address.length();
- memcpy(response->p_resp_data.cstr_address, port_address.ptr(), port_address.length());
- new_port->port_peer_name = port->port_peer_name;
- return new_port;
- }
- #if !(defined WIN_NT)
- static THREAD_ENTRY_DECLARE waitThread(THREAD_ENTRY_PARAM)
- {
- /**************************************
- *
- * w a i t T h r e a d
- *
- **************************************
- *
- * Functional description
- * Waits for processes started by standalone classic server (avoid zombies)
- *
- **************************************/
- while (procCount > 0)
- {
- int rc = wait(0);
- MutexLockGuard guard(waitThreadMutex, FB_FUNCTION);
- if (rc > 0) {
- --procCount;
- }
- }
- return 0;
- }
- #endif // !defined(WIN_NT)
- static void disconnect(rem_port* port)
- {
- /**************************************
- *
- * d i s c o n n e c t
- *
- **************************************
- *
- * Functional description
- * Break a remote connection.
- *
- **************************************/
- // SO_LINGER was turned off on the initial bind when the server was started.
- // This will force a reset to be sent to the client when the socket is closed.
- // We only want this behavior in the case of the server terminating
- // abnormally and not on an orderly shut down. Because of this, turn the
- // SO_LINGER option back on for the socket. The result of setsockopt isn't
- // too important at this stage since we are closing the socket anyway. This
- // is an attempt to return the socket to a state where a graceful shutdown can
- // occur.
- if (port->port_linger.l_onoff)
- {
- setsockopt(port->port_handle, SOL_SOCKET, SO_LINGER,
- (SCHAR*) &port->port_linger, sizeof(port->port_linger));
- }
- if (port->port_handle != INVALID_SOCKET)
- {
- shutdown(port->port_handle, 2);
- }
- MutexLockGuard guard(port_mutex, FB_FUNCTION);
- if (port->port_state == rem_port::DISCONNECTED)
- return;
- port->port_state = rem_port::DISCONNECTED;
- port->port_flags &= ~PORT_connecting;
- if (port->port_async)
- {
- disconnect(port->port_async);
- port->port_async = NULL;
- }
- port->port_context = NULL;
- // hvlad: delay closing of the server sockets to prevent its reuse
- // by another (newly accepted) port until next select() call. See
- // also select_wait() function.
- const bool delayClose = (port->port_server_flags && port->port_parent);
- // If this is a sub-port, unlink it from its parent
- port->unlinkParent();
- inet_ports->unRegisterPort(port);
- if (delayClose)
- {
- if (port->port_handle != INVALID_SOCKET)
- ports_to_close->push(port->port_handle);
- if (port->port_channel != INVALID_SOCKET)
- ports_to_close->push(port->port_channel);
- }
- else
- {
- SOCLOSE(port->port_handle);
- SOCLOSE(port->port_channel);
- }
- if (port->port_thread_guard && port->port_events_thread && !port->port_events_threadId.isCurrent())
- port->port_thread_guard->setWait(port->port_events_thread);
- else
- port->releasePort();
- #ifdef DEBUG
- if (INET_trace & TRACE_summary)
- {
- fprintf(stdout, "INET_count_send = %u packets\n", INET_count_send);
- fprintf(stdout, "INET_bytes_send = %u bytes\n", INET_bytes_send);
- fprintf(stdout, "INET_count_recv = %u packets\n", INET_count_recv);
- fprintf(stdout, "INET_bytes_recv = %u bytes\n", INET_bytes_recv);
- fflush(stdout);
- }
- #endif
- return;
- }
- static void force_close(rem_port* port)
- {
- /**************************************
- *
- * f o r c e _ c l o s e
- *
- **************************************
- *
- * Functional description
- * Forcebly close remote connection.
- *
- **************************************/
- if (port->port_async)
- abort_aux_connection(port->port_async);
- if (port->port_state != rem_port::PENDING)
- return;
- RefMutexGuard guard(*port->port_write_sync, FB_FUNCTION);
- port->port_state = rem_port::BROKEN;
- if (port->port_handle != INVALID_SOCKET)
- {
- shutdown(port->port_handle, 2);
- SOCLOSE(port->port_handle);
- }
- }
- static int cleanup_ports(const int, const int, void* /*arg*/)
- {
- /**************************************
- *
- * c l e a n u p _ p o r t s
- *
- **************************************
- *
- * Functional description
- * Shutdown all active connections
- * to allow correct shutdown.
- *
- **************************************/
- INET_shutting_down = true;
- inet_ports->closePorts();
- while (ports_to_close->hasData())
- {
- SOCKET s = ports_to_close->pop();
- SOCLOSE(s);
- }
- return 0;
- }
- #ifdef NO_FORK
- static int fork()
- {
- /**************************************
- *
- * f o r k ( N O _ F O R K )
- *
- **************************************
- *
- * Functional description
- * Hmmm.
- *
- **************************************/
- return 1;
- }
- #endif
- #ifdef WIN_NT
- static int wsaExitHandler(const int, const int, void*)
- {
- /**************************************
- *
- * w s a E x i t H a n d l e r
- *
- **************************************
- *
- * Functional description
- * Cleanup WSA.
- *
- **************************************/
- SleepEx(0, FALSE); // let select in other thread(s) shutdown gracefully
- WSACleanup();
- return 0;
- }
- static int fork(SOCKET old_handle, USHORT flag)
- {
- /**************************************
- *
- * f o r k ( W I N _ N T )
- *
- **************************************
- *
- * Functional description
- * Create a child process.
- *
- **************************************/
- TEXT name[MAXPATHLEN];
- GetModuleFileName(NULL, name, sizeof(name));
- HANDLE new_handle;
- if (!DuplicateHandle(GetCurrentProcess(), (HANDLE) old_handle,
- GetCurrentProcess(), &new_handle,
- 0, TRUE, DUPLICATE_SAME_ACCESS))
- {
- gds__log("INET/inet_error: fork/DuplicateHandle errno = %d", GetLastError());
- return 0;
- }
- string cmdLine;
- cmdLine.printf("%s -i -h %" HANDLEFORMAT"@%" ULONGFORMAT, name, new_handle, GetCurrentProcessId());
- STARTUPINFO start_crud;
- start_crud.cb = sizeof(STARTUPINFO);
- start_crud.lpReserved = NULL;
- start_crud.lpReserved2 = NULL;
- start_crud.cbReserved2 = 0;
- start_crud.lpDesktop = NULL;
- start_crud.lpTitle = NULL;
- start_crud.dwFlags = STARTF_FORCEOFFFEEDBACK;
- PROCESS_INFORMATION pi;
- if (CreateProcess(NULL, cmdLine.begin(), NULL, NULL, FALSE,
- (flag & SRVR_high_priority ?
- HIGH_PRIORITY_CLASS | DETACHED_PROCESS :
- NORMAL_PRIORITY_CLASS | DETACHED_PROCESS),
- NULL, NULL, &start_crud, &pi))
- {
- CloseHandle(pi.hThread);
- CloseHandle(pi.hProcess);
- // hvlad: child process will close our handle of just accepted socket
- return 1;
- }
- gds__log("INET/inet_error: fork/CreateProcess errno = %d", GetLastError());
- CloseHandle(new_handle);
- return 0;
- }
- THREAD_ENTRY_DECLARE forkThread(THREAD_ENTRY_PARAM arg)
- {
- const USHORT flag = (USHORT)(U_IPTR) arg;
- while (!INET_shutting_down)
- {
- if (WaitForSingleObject(forkEvent, INFINITE) != WAIT_OBJECT_0)
- break;
- while (!INET_shutting_down)
- {
- SOCKET s = 0;
- { // scope
- MutexLockGuard forkGuard(forkMutex, FB_FUNCTION);
- if (!forkSockets || forkSockets->getCount() == 0)
- break;
- s = (*forkSockets)[0];
- forkSockets->remove((FB_SIZE_T) 0);
- }
- fork(s, flag);
- SOCLOSE(s);
- }
- }
- return 0;
- }
- // Windows does not have an inet_aton function.
- bool inet_aton(const char* name, in_addr* address)
- {
- address->s_addr = inet_addr(name);
- return address->s_addr != INADDR_NONE;
- }
- #endif
- static rem_port* receive( rem_port* main_port, PACKET * packet)
- {
- /**************************************
- *
- * r e c e i v e
- *
- **************************************
- *
- * Functional description
- * Receive a message from a port or clients of a port. If the process
- * is a server and a connection request comes in, generate a new port
- * block for the client.
- *
- **************************************/
- // loop as long as we are receiving dummy packets, just
- // throwing them away--note that if we are a server we won't
- // be receiving them, but it is better to check for them at
- // this level rather than try to catch them in all places where
- // this routine is called
- do {
- if (!xdr_protocol(main_port->port_receive, packet))
- {
- packet->p_operation = main_port->port_partial_data ? op_partial : op_exit;
- if (packet->p_operation == op_exit)
- main_port->port_state = rem_port::BROKEN;
- main_port->port_partial_data = false;
- break;
- }
- #ifdef DEBUG
- {
- static ULONG op_rec_count = 0;
- op_rec_count++;
- if (INET_trace & TRACE_operations)
- {
- fprintf(stdout, "%04u: OP Recd %5u opcode %d\n",
- inet_debug_timer(),
- op_rec_count, packet->p_operation);
- fflush(stdout);
- }
- }
- #endif
- } while (packet->p_operation == op_dummy);
- return main_port;
- }
- static bool select_multi(rem_port* main_port, UCHAR* buffer, SSHORT bufsize, SSHORT* length,
- RemPortPtr& port)
- {
- /**************************************
- *
- * s e l e c t _ m u l t i
- *
- **************************************
- *
- * Functional description
- * Receive an IP packet from a port or clients of a port.
- * Used only by the multiclient server on main server's port.
- * If a connection request comes in, generate a new port
- * block for the client.
- *
- **************************************/
- // This code is used to test error handling in main server loop
- #ifdef NEVERDEF
- static int dummyCnt = 0;
- if (++dummyCnt % 64 == 0)
- (Arg::Gds(isc_random) << "Simulated select_multi error").raise();
- #endif
- for (;;)
- {
- select_port(main_port, &INET_select, port);
- if (port == main_port && (port->port_server_flags & SRVR_multi_client))
- {
- if (INET_shutting_down)
- {
- if (main_port->port_state == rem_port::PENDING)
- {
- main_port->port_state = rem_port::BROKEN;
- shutdown(main_port->port_handle, 2);
- SOCLOSE(main_port->port_handle);
- }
- }
- else if ((port = select_accept(main_port)))
- {
- if (!REMOTE_inflate(port, packet_receive, buffer, bufsize, length))
- {
- *length = 0;
- }
- #ifdef WIRE_COMPRESS_SUPPORT
- if (port->port_z_data)
- INET_select->setZDataPort(port);
- #endif
- return (*length) ? true : false;
- }
- continue;
- }
- if (port)
- {
- if (port->port_dummy_timeout < 0)
- {
- port->port_dummy_timeout = port->port_dummy_packet_interval;
- if (port->port_flags & PORT_async)
- continue;
- *length = 0;
- return true;
- }
- if (!REMOTE_inflate(port, packet_receive, buffer, bufsize, length))
- {
- if (port->port_flags & (PORT_disconnect | PORT_connecting))
- {
- continue;
- }
- *length = 0;
- }
- #ifdef WIRE_COMPRESS_SUPPORT
- if (port->port_z_data)
- INET_select->setZDataPort(port);
- #endif
- return (*length) ? true : false;
- }
- if (!select_wait(main_port, &INET_select))
- {
- port = NULL;
- return false;
- }
- }
- }
- static rem_port* select_accept( rem_port* main_port)
- {
- /**************************************
- *
- * s e l e c t _ a c c e p t
- *
- **************************************
- *
- * Functional description
- * Accept a new connection request.
- *
- **************************************/
- rem_port* const port = alloc_port(main_port);
- inet_ports->registerPort(port);
- port->port_handle = os_utils::accept(main_port->port_handle, NULL, NULL);
- if (port->port_handle == INVALID_SOCKET)
- {
- inet_error(true, port, "accept", isc_net_connect_err, INET_ERRNO);
- }
- setKeepAlive(port->port_handle);
- port->port_flags |= PORT_server;
- if (main_port->port_server_flags & SRVR_thread_per_port)
- {
- port->port_server_flags = (SRVR_server | SRVR_inet | SRVR_thread_per_port);
- return port;
- }
- return 0;
- }
- static void select_port(rem_port* main_port, Select* selct, RemPortPtr& port)
- {
- /**************************************
- *
- * s e l e c t _ p o r t
- *
- **************************************
- *
- * Functional description
- * Select a descriptor that is ready to read
- * and return the port block. Additionally,
- * check if a port's keepalive timer has
- * expired and return the port block so that
- * a keepalive packet can be queued. Return
- * NULL if none are active.
- *
- **************************************/
- MutexLockGuard guard(port_mutex, FB_FUNCTION);
- while (true)
- {
- Select::HandleState result = selct->checkNext(port);
- if (!port)
- return;
- switch (result)
- {
- case Select::SEL_BAD:
- if (port->port_state == rem_port::BROKEN || (port->port_flags & PORT_connecting))
- continue;
- if (port->port_flags & PORT_async)
- continue;
- return;
- case Select::SEL_DISCONNECTED:
- continue;
- case Select::SEL_READY:
- port->port_dummy_timeout = port->port_dummy_packet_interval;
- return;
- default:
- break;
- }
- if (port->port_dummy_timeout < 0)
- return;
- }
- }
- static bool select_wait( rem_port* main_port, Select* selct)
- {
- /**************************************
- *
- * s e l e c t _ w a i t
- *
- **************************************
- *
- * Functional description
- * Select interesting descriptors from
- * port blocks and wait for something
- * to read from them.
- *
- **************************************/
- struct timeval timeout;
- bool checkPorts = false;
- for (;;)
- {
- selct->clear();
- bool found = false;
- // Use the time interval between select() calls to expire
- // keepalive timers on all ports.
- time_t delta_time;
- if (selct->slct_time)
- {
- delta_time = time(NULL) - selct->slct_time;
- selct->slct_time += delta_time;
- }
- else
- {
- delta_time = 0;
- selct->slct_time = time(NULL);
- }
- { // port_mutex scope
- MutexLockGuard guard(port_mutex, FB_FUNCTION);
- while (ports_to_close->hasData())
- {
- SOCKET s = ports_to_close->pop();
- SOCLOSE(s);
- }
- for (rem_port* port = main_port; port; port = port->port_next)
- {
- if (port->port_state == rem_port::PENDING &&
- // don't wait on still listening (not connected) async port
- !(port->port_handle == INVALID_SOCKET && (port->port_flags & PORT_async)))
- {
- // Adjust down the port's keepalive timer.
- if (port->port_dummy_packet_interval)
- {
- port->port_dummy_timeout -= delta_time;
- }
- if (checkPorts)
- {
- // select() returned EBADF\WSAENOTSOCK - we have a broken socket
- // in current fdset. Search and return it to caller to close
- // broken connection correctly
- struct linger lngr;
- socklen_t optlen = sizeof(lngr);
- const bool badSocket =
- #ifdef WIN_NT
- false;
- #else
- (port->port_handle < 0 || port->port_handle >= FD_SETSIZE);
- #endif
- if (badSocket || getsockopt(port->port_handle,
- SOL_SOCKET, SO_LINGER, (SCHAR*) &lngr, &optlen) != 0)
- {
- if (badSocket || INET_ERRNO == NOTASOCKET)
- {
- // not a socket, strange !
- gds__log("INET/select_wait: found \"not a socket\" socket : %" HANDLEFORMAT,
- port->port_handle);
- // this will lead to receive() which will break bad connection
- selct->clear();
- if (!badSocket)
- {
- selct->set(port->port_handle);
- }
- return true;
- }
- }
- }
- // if process is shuting down - don't listen on main port
- if (!INET_shutting_down || port != main_port)
- {
- selct->set(port->port_handle);
- found = true;
- }
- }
- }
- checkPorts = false;
- } // port_mutex scope
- if (!found)
- {
- if (!INET_shutting_down && (main_port->port_server_flags & SRVR_multi_client))
- gds__log("INET/select_wait: client rundown complete, server exiting");
- return false;
- }
- for (;;)
- {
- // Before waiting for incoming packet, check for server shutdown
- if (tryStopMainThread && tryStopMainThread())
- {
- // this is not server port any more
- main_port->port_server_flags &= ~SRVR_multi_client;
- return false;
- }
- // Some platforms change the timeout in the select call.
- // Reset timeout for each iteration to avoid problems.
- timeout.tv_sec = SELECT_TIMEOUT;
- timeout.tv_usec = 0;
- selct->select(&timeout);
- const int inetErrNo = INET_ERRNO;
- //if (INET_shutting_down) {
- // return false;
- //}
- if (selct->getCount() != -1)
- {
- RemPortPtr p(main_port);
- selct->checkStart(p);
- // if selct->slct_count is zero it means that we timed out of
- // select with nothing to read or accept, so clear the fd_set
- // bit as this value is undefined on some platforms (eg. HP-UX),
- // when the select call times out. Once these bits are cleared
- // they can be used in select_port()
- if (selct->getCount() == 0)
- {
- MutexLockGuard guard(port_mutex, FB_FUNCTION);
- for (rem_port* port = main_port; port; port = port->port_next)
- {
- selct->unset(port->port_handle);
- }
- }
- return true;
- }
- if (INTERRUPT_ERROR(inetErrNo))
- continue;
- if (inetErrNo == NOTASOCKET)
- {
- checkPorts = true;
- break;
- }
- gds__log("INET/select_wait: select failed, errno = %d", inetErrNo);
- return false;
- } // for (;;)
- }
- }
- static int send_full( rem_port* port, PACKET * packet)
- {
- /**************************************
- *
- * s e n d _ f u l l
- *
- **************************************
- *
- * Functional description
- * Send a packet across a port to another process.
- *
- **************************************/
- if (!xdr_protocol(port->port_send, packet))
- return false;
- #ifdef DEBUG
- { // scope
- static ULONG op_sent_count = 0;
- op_sent_count++;
- if (INET_trace & TRACE_operations)
- {
- fprintf(stdout, "%05u: OP Sent %5u opcode %d\n", inet_debug_timer(),
- op_sent_count, packet->p_operation);
- fflush(stdout);
- }
- } // end scope
- #endif
- return REMOTE_deflate(port->port_send, inet_write, packet_send, true);
- }
- static int send_partial( rem_port* port, PACKET * packet)
- {
- /**************************************
- *
- * s e n d _ p a r t i a l
- *
- **************************************
- *
- * Functional description
- * Send a packet across a port to another process.
- *
- **************************************/
- #ifdef DEBUG
- { // scope
- static ULONG op_sentp_count = 0;
- op_sentp_count++;
- if (INET_trace & TRACE_operations)
- {
- fprintf(stdout, "%05u: OP Sent %5u opcode %d (partial)\n", inet_debug_timer(),
- op_sentp_count, packet->p_operation);
- fflush(stdout);
- }
- } // end scope
- #endif
- return xdr_protocol(port->port_send, packet);
- }
- RemoteXdr* xdrinet_create(rem_port* port, UCHAR* buffer, USHORT length, enum xdr_op x_op)
- {
- /**************************************
- *
- * x d r i n e t _ c r e a t e
- *
- **************************************
- *
- * Functional description
- * Initialize an XDR stream.
- *
- **************************************/
- RemoteXdr* xdrs = FB_NEW InetXdr;
- xdrs->x_public = port;
- xdrs->create(reinterpret_cast<SCHAR*>(buffer), length, x_op);
- return xdrs;
- }
- #ifdef HAVE_SETITIMER
- static void alarm_handler( int x)
- {
- /**************************************
- *
- * a l a r m _ h a n d l e r
- *
- **************************************
- *
- * Functional description
- * Handle an alarm clock interrupt. If we were waiting on
- * a semaphone, zap it.
- *
- **************************************/
- }
- #endif
- void get_peer_info(rem_port* port)
- {
- /**************************************
- *
- * g e t _ p e e r _ i n f o
- *
- **************************************
- *
- * Functional description
- * Port just connected. Obtain some info about connection and peer.
- *
- **************************************/
- port->port_protocol_id = "TCPv4";
- SockAddr address;
- if (address.getpeername(port->port_handle) == 0)
- {
- address.unmapV4(); // convert mapped IPv4 to regular IPv4
- char host[64]; // 32 digits, 7 colons, 1 trailing null byte
- char serv[16];
- int nameinfo = getnameinfo(address.ptr(), address.length(), host, sizeof(host),
- serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV);
- if (!nameinfo)
- port->port_address.printf("%s/%s", host, serv);
- if (address.family() == AF_INET6)
- port->port_protocol_id = "TCPv6";
- }
- }
- static void inet_gen_error(bool releasePort, rem_port* port, const Arg::StatusVector& v)
- {
- /**************************************
- *
- * i n e t _ g e n _ e r r o r
- *
- **************************************
- *
- * Functional description
- * An error has occurred. Mark the port as broken.
- * Format the status vector if there is one and
- * save the status vector strings in a permanent place.
- *
- **************************************/
- port->port_state = rem_port::BROKEN;
- string node_name(port->port_connection ? port->port_connection->str_data : "(unknown)");
- if (releasePort)
- {
- disconnect(port);
- }
- Arg::Gds error(isc_network_error);
- error << Arg::Str(node_name) << v;
- error.raise();
- }
- bool_t InetXdr::x_getbytes(SCHAR* buff, unsigned bytecount)
- {
- /**************************************
- *
- * i n e t _ g e t b y t e s
- *
- **************************************
- *
- * Functional description
- * Get a bunch of bytes from a memory stream if it fits.
- *
- **************************************/
- if (x_public->port_flags & PORT_server)
- return REMOTE_getbytes(this, buff, bytecount);
- // Use memcpy to optimize bulk transfers.
- while (bytecount > sizeof(ISC_QUAD))
- {
- if (x_handy >= bytecount)
- {
- memcpy(buff, x_private, bytecount);
- x_private += bytecount;
- x_handy -= bytecount;
- return TRUE;
- }
- if (x_handy > 0)
- {
- memcpy(buff, x_private, x_handy);
- x_private += x_handy;
- buff += x_handy;
- bytecount -= x_handy;
- x_handy = 0;
- }
- if (!inet_read(this))
- return FALSE;
- }
- // Scalar values and bulk transfer remainder fall thru
- // to be moved byte-by-byte to avoid memcpy setup costs.
- if (!bytecount)
- return TRUE;
- if (x_handy >= bytecount)
- {
- x_handy -= bytecount;
- while (bytecount--)
- *buff++ = *x_private++;
- return TRUE;
- }
- while (bytecount--)
- {
- if (x_handy == 0 && !inet_read(this))
- return FALSE;
- *buff++ = *x_private++;
- --x_handy;
- }
- return TRUE;
- }
- static void inet_error(bool releasePort, rem_port* port, const TEXT* function, ISC_STATUS operation, int status)
- {
- /**************************************
- *
- * i n e t _ e r r o r
- *
- **************************************
- *
- * Functional description
- * An I/O error has occurred. Call
- * inet_gen_error with the appropriate args
- * to format the status vector if any.
- *
- **************************************/
- if (status)
- {
- if (port->port_state == rem_port::PENDING)
- {
- string err;
- err.printf("INET/inet_error: %s errno = %d", function, status);
- if (port->port_peer_name.hasData() || port->port_address.hasData())
- {
- err.append(port->port_flags & PORT_async ? ", aux " : ", ");
- err.append(port->port_server_flags ? "client" : "server");
- if (port->port_peer_name.hasData())
- {
- err.append(" host = ");
- err.append(port->port_peer_name);
- }
- if (port->port_address.hasData())
- {
- if (port->port_peer_name.hasData())
- err.append(",");
- err.append(" address = ");
- err.append(port->port_address);
- }
- }
- if (port->port_user_name.hasData())
- {
- err.append(", user = ");
- err.append(port->port_user_name);
- }
- // Address could contain percent sign inside, therefore make
- // sure error string not used as printf format string.
- gds__log("%s", err.c_str());
- }
- inet_gen_error(releasePort, port, Arg::Gds(operation) << SYS_ERR(status));
- }
- else
- {
- // No status value, just format the basic arguments.
- inet_gen_error(releasePort, port, Arg::Gds(operation));
- }
- }
- bool_t InetXdr::x_putbytes(const SCHAR* buff, unsigned bytecount)
- {
- /**************************************
- *
- * i n e t _ p u t b y t e s
- *
- **************************************
- *
- * Functional description
- * Put a bunch of bytes to a memory stream if it fits.
- *
- **************************************/
- // Use memcpy to optimize bulk transfers.
- while (bytecount > sizeof(ISC_QUAD))
- {
- if (x_handy >= bytecount)
- {
- memcpy(x_private, buff, bytecount);
- x_private += bytecount;
- x_handy -= bytecount;
- return TRUE;
- }
- if (x_handy > 0)
- {
- memcpy(x_private, buff, x_handy);
- x_private += x_handy;
- buff += x_handy;
- bytecount -= x_handy;
- x_handy = 0;
- }
- if (!REMOTE_deflate(this, inet_write, packet_send, false))
- {
- return FALSE;
- }
- }
- // Scalar values and bulk transfer remainder fall thru
- // to be moved byte-by-byte to avoid memcpy setup costs.
- if (!bytecount)
- return TRUE;
- if (x_handy >= bytecount)
- {
- x_handy -= bytecount;
- while (bytecount--)
- *x_private++ = *buff++;
- return TRUE;
- }
- while (bytecount--)
- {
- if (x_handy == 0 && !REMOTE_deflate(this, inet_write, packet_send, false))
- return FALSE;
- --x_handy;
- *x_private++ = *buff++;
- }
- return TRUE;
- }
- static bool inet_read( RemoteXdr* xdrs)
- {
- /**************************************
- *
- * i n e t _ r e a d
- *
- **************************************
- *
- * Functional description
- * Read a buffer full of data. If we receive a bad packet,
- * send the moral equivalent of a NAK and retry. ACK all
- * partial packets. Don't ACK the last packet -- the next
- * message sent will handle this.
- *
- **************************************/
- rem_port* port = xdrs->x_public;
- char* p = xdrs->x_base;
- const char* const end = p + INET_remote_buffer;
- // If buffer is not completely empty, slide down what's left
- if (xdrs->x_handy > 0)
- {
- memmove(p, xdrs->x_private, xdrs->x_handy);
- p += xdrs->x_handy;
- }
- SSHORT length = end - p;
- port->port_z_data = false;
- if (!REMOTE_inflate(port, packet_receive2, (UCHAR*)p, length, &length))
- return false;
- p += length;
- xdrs->x_handy = (SCHAR *) p - xdrs->x_base;
- xdrs->x_private = xdrs->x_base;
- return true;
- }
- static bool packet_receive2(rem_port* port, UCHAR* p, SSHORT bufSize, SSHORT* length)
- {
- *length = 0;
- while (true)
- {
- SSHORT l = bufSize - *length;
- if (!packet_receive(port, p + *length, l, &l))
- return false;
- if (l >= 0)
- {
- *length += l;
- break;
- }
- *length -= l;
- if (!packet_send(port, 0, 0))
- return false;
- }
- return true;
- }
- static rem_port* inet_try_connect(PACKET* packet,
- Rdb* rdb,
- const PathName& file_name,
- const TEXT* node_name,
- ClumpletReader& dpb,
- RefPtr<const Config>* config,
- const PathName* ref_db_name,
- int af)
- {
- /**************************************
- *
- * i n e t _ t r y _ c o n n e c t
- *
- **************************************
- *
- * Functional description
- * Given a packet with formatted protocol infomation,
- * set header information and try the connection.
- *
- * If a connection is established, return a port block, otherwise
- * return NULL.
- *
- **************************************/
- P_CNCT* cnct = &packet->p_cnct;
- packet->p_operation = op_connect;
- cnct->p_cnct_operation = 0;
- cnct->p_cnct_cversion = CONNECT_VERSION3;
- cnct->p_cnct_client = ARCHITECTURE;
- const PathName& cnct_file(ref_db_name ? (*ref_db_name) : file_name);
- cnct->p_cnct_file.cstr_length = (ULONG) cnct_file.length();
- cnct->p_cnct_file.cstr_address = reinterpret_cast<const UCHAR*>(cnct_file.c_str());
- // If we can't talk to a server, punt. Let somebody else generate
- // an error. status_vector will have the network error info.
- rem_port* port = NULL;
- try
- {
- port = INET_connect(node_name, packet, false, &dpb, config, af);
- }
- catch (const Exception&)
- {
- delete rdb;
- throw;
- }
- // Get response packet from server.
- rdb->rdb_port = port;
- port->port_context = rdb;
- if (!port->receive(packet))
- {
- rdb->rdb_port = NULL;
- delete rdb;
- inet_error(true, port, "receive in try_connect", isc_net_connect_err, INET_ERRNO);
- }
- return port;
- }
- static bool inet_write(RemoteXdr* xdrs)
- {
- /**************************************
- *
- * i n e t _ w r i t e
- *
- **************************************
- *
- * Functional description
- * Write a buffer full of data.
- *
- **************************************/
- // Encode the data portion of the packet
- rem_port* port = xdrs->x_public;
- const char* p = xdrs->x_base;
- USHORT length = xdrs->x_private - p;
- // Send data in manageable hunks. If a packet is partial, indicate
- // that with a negative length. A positive length marks the end.
- while (length)
- {
- const SSHORT l = (SSHORT) MIN(length, INET_remote_buffer);
- length -= l;
- if (!packet_send(port, p, (SSHORT) (length ? -l : l)))
- return false;
- p += l;
- }
- xdrs->x_private = xdrs->x_base;
- xdrs->x_handy = INET_remote_buffer;
- return true;
- }
- #ifdef DEBUG
- static void packet_print(const TEXT* string, const UCHAR* packet, int length, ULONG counter)
- {
- /**************************************
- *
- * p a c k e t _ p r i n t
- *
- **************************************
- *
- * Functional description
- * Print a summary of packet.
- *
- **************************************/
- int sum = 0;
- for (int l = length; l > 0; --l)
- sum += *packet++;
- fprintf(stdout, "%05u: PKT %s\t(%u): length = %4d, checksum = %d\n",
- inet_debug_timer(), string, counter, length, sum);
- fflush(stdout);
- }
- #endif
- static bool packet_receive(rem_port* port, UCHAR* buffer, SSHORT buffer_length, SSHORT* length)
- {
- /**************************************
- *
- * p a c k e t _ r e c e i v e
- *
- **************************************
- *
- * Functional description
- * Receive a packet and pass on it's goodness. If it's good,
- * return true and the reported length of the packet, and update
- * the receive sequence number. If it's bad, return false. If it's
- * a duplicate message, just ignore it.
- *
- **************************************/
- if (port->port_flags & PORT_disconnect) {
- return false;
- }
- timeval timeout;
- timeout.tv_usec = 0;
- timeval* time_ptr = NULL;
- if (port->port_protocol == 0)
- {
- // If the protocol is 0 we are still in the process of establishing
- // a connection. Add a time out to the wait.
- timeout.tv_sec = port->port_connect_timeout;
- time_ptr = &timeout;
- }
- else if (port->port_dummy_packet_interval > 0)
- {
- // Set the time interval for sending dummy packets to the client
- timeout.tv_sec = port->port_dummy_packet_interval;
- time_ptr = &timeout;
- }
- // On Linux systems (and possibly others too) select will eventually
- // change timout values so save it here for later reuse.
- // Thanks to Brad Pepers who reported this bug FSG 3 MAY 2001
- const timeval savetime = timeout;
- const SOCKET ph = port->port_handle;
- if (ph == INVALID_SOCKET)
- {
- const bool releasePort = (port->port_flags & PORT_server);
- if (!(port->port_flags & PORT_disconnect) && releasePort)
- inet_error(true, port, "invalid socket in packet_receive", isc_net_read_err, EINVAL);
- return false;
- }
- // Unsed to send a dummy packet, but too big to be defined in the loop.
- PACKET packet;
- int n = 0;
- int inetErrNo;
- LocalStatus ls;
- CheckStatusWrapper st(&ls);
- for (;;)
- {
- // Implement an error-detection protocol to ensure that the client
- // is still there. Use the select() call with a timeout to wait on
- // the connection for an incoming packet. If none comes within a
- // suitable time interval, write a dummy packet on the connection.
- // If the client is not there, an error will be returned on the write.
- // If the client is there, the dummy packet will be ignored by all
- // InterBase clients V4 or greater. This protocol will detect when
- // clients are lost abnormally through reboot or network disconnect.
- // Don't send op_dummy packets on aux port; the server won't
- // read them because it only writes to aux ports.
- if ( !(port->port_flags & PORT_async) )
- {
- Select slct;
- slct.set(ph);
- int slct_count;
- for (;;)
- {
- slct.select(time_ptr);
- slct_count = slct.getCount();
- inetErrNo = INET_ERRNO;
- // restore original timeout value FSG 3 MAY 2001
- timeout = savetime;
- if (slct_count != -1 || !INTERRUPT_ERROR(inetErrNo))
- {
- break;
- }
- }
- if (slct_count == -1)
- {
- if (!(port->port_flags & PORT_disconnect))
- {
- try
- {
- inet_error(false, port, "select in packet_receive", isc_net_read_err, inetErrNo);
- }
- catch (const Exception&) { }
- }
- return false;
- }
- if (!slct_count)
- {
- if (port->port_protocol == 0)
- return false;
- #ifdef DEBUG
- if (INET_trace & TRACE_operations)
- {
- fprintf(stdout, "%05u: OP Sent: op_dummy\n", inet_debug_timer());
- fflush(stdout);
- }
- #endif
- packet.p_operation = op_dummy;
- if (!send_full(port, &packet))
- {
- return false;
- }
- continue;
- }
- }
- n = recv(port->port_handle, reinterpret_cast<char*>(buffer), buffer_length, 0);
- inetErrNo = INET_ERRNO;
- // decrypt
- if (n > 0 && port->port_crypt_plugin)
- {
- port->port_crypt_plugin->decrypt(&st, n, buffer, buffer);
- if (st.getState() & Firebird::IStatus::STATE_ERRORS)
- {
- status_exception::raise(&st);
- }
- }
- if (n != -1 || !INTERRUPT_ERROR(inetErrNo))
- break;
- }
- if (n <= 0 && (port->port_flags & PORT_disconnect)) {
- return false;
- }
- if (n == -1)
- {
- try
- {
- inet_error(false, port, "read", isc_net_read_err, inetErrNo);
- }
- catch (const Exception&) { }
- return false;
- }
- if (!n)
- {
- port->port_state = rem_port::BROKEN;
- return false;
- }
- #ifdef DEBUG
- { // scope
- INET_count_recv++;
- INET_bytes_recv += n;
- if (INET_trace & TRACE_packets)
- packet_print("receive", buffer, n, INET_count_recv);
- INET_force_error--;
- if (INET_force_error == 0)
- {
- INET_force_error = 1;
- try
- {
- inet_error(false, port, "simulated error - read", isc_net_read_err);
- }
- catch (const Exception&) { }
- return false;
- }
- } // end scope
- #endif
- port->port_rcv_packets++;
- port->port_rcv_bytes += n;
- *length = n;
- return true;
- }
- static bool packet_send( rem_port* port, const SCHAR* buffer, SSHORT buffer_length)
- {
- /**************************************
- *
- * p a c k e t _ s e n d
- *
- **************************************
- *
- * Functional description
- * Send some data on it's way.
- *
- **************************************/
- SSHORT length = buffer_length;
- const char* data = buffer;
- // encrypt
- HalfStaticArray<char, BUFFER_TINY> b;
- if (port->port_crypt_plugin && port->port_crypt_complete)
- {
- LocalStatus ls;
- CheckStatusWrapper st(&ls);
- char* d = b.getBuffer(buffer_length);
- port->port_crypt_plugin->encrypt(&st, buffer_length, data, d);
- if (st.getState() & Firebird::IStatus::STATE_ERRORS)
- {
- status_exception::raise(&st);
- }
- data = d;
- }
- while (length)
- {
- #ifdef DEBUG
- if (INET_trace & TRACE_operations)
- {
- fprintf(stdout, "Before Send\n");
- fflush(stdout);
- }
- #endif
- SSHORT n = send(port->port_handle, data, length, FB_SEND_FLAGS);
- #if COMPRESS_DEBUG > 1
- fprintf(stderr, "send(%d, %p, %d, FB_SEND_FLAGS) == %d\n", port->port_handle, data, length, n);
- #endif
- #ifdef DEBUG
- if (INET_trace & TRACE_operations)
- {
- fprintf(stdout, "After Send n is %d\n", n);
- fflush(stdout);
- }
- #endif
- if (n == length) {
- break;
- }
- if (n == -1)
- {
- if (INTERRUPT_ERROR(INET_ERRNO)) {
- continue;
- }
- try
- {
- inet_error(false, port, "send", isc_net_write_err, INET_ERRNO);
- }
- catch (const Exception&) { }
- return false;
- }
- data += n;
- length -= n;
- }
- #ifdef HAVE_SETITIMER
- struct itimerval internal_timer, client_timer;
- struct sigaction internal_handler, client_handler;
- #endif // HAVE_SETITIMER
- if ((port->port_flags & PORT_async) && !(port->port_flags & PORT_no_oob))
- {
- int count = 0;
- SSHORT n;
- int inetErrNo = 0;
- const char* b = buffer;
- while ((n = send(port->port_handle, b, 1, MSG_OOB | FB_SEND_FLAGS)) == -1 &&
- (INET_ERRNO == ENOBUFS || INTERRUPT_ERROR(INET_ERRNO)))
- {
- inetErrNo = INET_ERRNO;
- if (count++ > 20) {
- break;
- }
- #ifndef HAVE_SETITIMER
- #ifdef WIN_NT
- SleepEx(50, TRUE);
- #else
- sleep(1);
- #endif
- } // end of while() loop for systems without setitimer.
- #else // HAVE_SETITIMER
- if (count == 1)
- {
- // Wait in a loop until the lock becomes available
- internal_timer.it_interval.tv_sec = 0;
- internal_timer.it_interval.tv_usec = 0;
- internal_timer.it_value.tv_sec = 0;
- internal_timer.it_value.tv_usec = 0;
- setitimer(ITIMER_REAL, &internal_timer, &client_timer);
- internal_handler.sa_handler = alarm_handler;
- sigemptyset(&internal_handler.sa_mask);
- internal_handler.sa_flags = SA_RESTART;
- sigaction(SIGALRM, &internal_handler, &client_handler);
- }
- internal_timer.it_value.tv_sec = 0;
- internal_timer.it_value.tv_usec = 50000;
- setitimer(ITIMER_REAL, &internal_timer, NULL);
- pause();
- } // end of while() loop for systems with setitimer
- if (count)
- {
- // Restore user's outstanding alarm request and handler
- internal_timer.it_value.tv_sec = 0;
- internal_timer.it_value.tv_usec = 0;
- setitimer(ITIMER_REAL, &internal_timer, NULL);
- sigaction(SIGALRM, &client_handler, NULL);
- setitimer(ITIMER_REAL, &client_timer, NULL);
- }
- #endif // HAVE_SETITIMER
- if (n == -1)
- {
- try
- {
- inet_error(false, port, "send/oob", isc_net_write_err, inetErrNo);
- }
- catch (const Exception&) { }
- return false;
- }
- }
- #ifdef DEBUG
- { // scope
- INET_count_send++;
- INET_bytes_send += buffer_length;
- if (INET_trace & TRACE_packets)
- packet_print("send", (const UCHAR*) buffer, buffer_length, INET_count_send);
- INET_force_error--;
- if (INET_force_error == 0)
- {
- INET_force_error = 1;
- try
- {
- inet_error(false, port, "simulated error - send", isc_net_write_err, 0);
- }
- catch (const Exception&) { }
- return false;
- }
- } // end scope
- #endif
- port->port_snd_packets++;
- port->port_snd_bytes += buffer_length;
- return true;
- }
- static bool setNoNagleOption(rem_port* port)
- {
- /**************************************
- *
- * s e t N o N a g l e O p t i o n
- *
- **************************************
- *
- * Functional description
- * Set TCP_NODELAY, return false
- * in case of unexpected error
- *
- **************************************/
- if (port->getPortConfig()->getTcpNoNagle())
- {
- int optval = TRUE;
- int n = setsockopt(port->port_handle, IPPROTO_TCP, TCP_NODELAY,
- (SCHAR*) &optval, sizeof(optval));
- if (n == -1)
- {
- return false;
- }
- }
- return true;
- }
- static bool setKeepAlive(SOCKET s)
- {
- /**************************************
- *
- * s e t K e e p A l i v e
- *
- **************************************
- *
- * Functional description
- * Set SO_KEEPALIVE, return false
- * in case of unexpected error
- *
- **************************************/
- int optval = 1;
- int n = setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
- (SCHAR*) &optval, sizeof(optval));
- return n != -1;
- }
- void setStopMainThread(FPTR_INT func)
- {
- /**************************************
- *
- * s e t S t o p M a i n T h r e a d
- *
- **************************************
- *
- * Functional description
- * Set function called by main thread
- * in order to check for shutdown.
- *
- **************************************/
- tryStopMainThread = func;
- }
- namespace os_utils
- {
- // force socket descriptor to have SOCK_CLOEXEC set
- SOCKET socket(int domain, int type, int protocol)
- {
- #ifdef WIN_NT
- return ::socket(domain, type, protocol);
- #else
- int fd;
- #if HAVE_DECL_SOCK_CLOEXEC
- do {
- fd = ::socket(domain, type | SOCK_CLOEXEC, protocol);
- } while (fd < 0 && SYSCALL_INTERRUPTED(errno));
- if (fd < 0 && errno == EINVAL) // probably SOCK_CLOEXEC not accepted
- #endif
- {
- do {
- fd = ::socket(domain, type, protocol);
- } while (fd < 0 && SYSCALL_INTERRUPTED(errno));
- }
- setCloseOnExec(fd);
- return fd;
- #endif
- }
- // force socket descriptor to have SOCK_CLOEXEC set
- SOCKET accept(SOCKET sockfd, struct sockaddr* addr, socklen_t* addrlen)
- {
- #ifdef WIN_NT
- return ::accept(sockfd, addr, addrlen);
- #else
- int fd;
- #if defined(HAVE_ACCEPT4) && HAVE_DECL_SOCK_CLOEXEC
- do {
- fd = ::accept4(sockfd, addr, addrlen, SOCK_CLOEXEC);
- } while (fd < 0 && SYSCALL_INTERRUPTED(errno));
- if (fd < 0 && errno == EINVAL) // probably SOCK_CLOEXEC not accepted
- #endif
- {
- do {
- fd = ::accept(sockfd, addr, addrlen);
- } while (fd < 0 && SYSCALL_INTERRUPTED(errno));
- }
- setCloseOnExec(fd);
- return fd;
- #endif
- }
- } // namespace os_utils
|