1 // Copyright (c) 1999-2018 David Muse
2 // See the COPYING file for more information
3 
4 #include <rudiments/protocolentry.h>
5 #include <rudiments/charstring.h>
6 #include <rudiments/bytestring.h>
7 #include <rudiments/error.h>
8 #include <rudiments/stdio.h>
9 
10 #include <rudiments/private/winsock.h>
11 
12 // for protoent, functions
13 #ifdef RUDIMENTS_HAVE_NETDB_H
14 	#include <netdb.h>
15 #endif
16 
17 #ifdef RUDIMENTS_HAVE_STDLIB_H
18 	#include <stdlib.h>
19 #endif
20 
21 #define MAXBUFFER	(32*1024)
22 
23 class protocolentryprivate {
24 	friend class protocolentry;
25 	private:
26 		protoent	*_pe;
27 		#if defined(RUDIMENTS_HAVE_GETPROTOBYNAME_R) || \
28 				defined(RUDIMENTS_HAVE_GETPROTOBYNUMBER_R)
29 			protoent	_pebuffer;
30 			char		*_buffer;
31 		#endif
32 };
33 
34 // LAME: not in the class
35 #if (!defined(RUDIMENTS_HAVE_GETPROTOBYNAME_R) || \
36 	!defined(RUDIMENTS_HAVE_GETPROTOBYNUMBER_R))
37 static threadmutex	*_pemutex;
38 #endif
39 
protocolentry()40 protocolentry::protocolentry() {
41 	pvt=new protocolentryprivate;
42 	winsock::initWinsock();
43 	pvt->_pe=NULL;
44 	#if defined(RUDIMENTS_HAVE_GETPROTOBYNAME_R) && \
45 		defined(RUDIMENTS_HAVE_GETPROTOBYNUMBER_R)
46 		bytestring::zero(&pvt->_pebuffer,sizeof(pvt->_pebuffer));
47 		pvt->_buffer=NULL;
48 	#endif
49 }
50 
protocolentry(const protocolentry & p)51 protocolentry::protocolentry(const protocolentry &p) {
52 	pvt=new protocolentryprivate;
53 	winsock::initWinsock();
54 	initialize(p.getName());
55 }
56 
operator =(const protocolentry & p)57 protocolentry &protocolentry::operator=(const protocolentry &p) {
58 	if (this!=&p) {
59 		initialize(p.getName());
60 	}
61 	return *this;
62 }
63 
~protocolentry()64 protocolentry::~protocolentry() {
65 	#if defined(RUDIMENTS_HAVE_GETPROTOBYNAME_R) && \
66 		defined(RUDIMENTS_HAVE_GETPROTOBYNUMBER_R)
67 		delete[] pvt->_buffer;
68 	#endif
69 	delete pvt;
70 }
71 
getName() const72 const char *protocolentry::getName() const {
73 	return (pvt->_pe)?pvt->_pe->p_name:NULL;
74 }
75 
getAliasList() const76 const char * const *protocolentry::getAliasList() const {
77 	return (pvt->_pe &&
78 		pvt->_pe->p_aliases &&
79 		pvt->_pe->p_aliases[0])?
80 		pvt->_pe->p_aliases:NULL;
81 }
82 
getNumber() const83 int32_t protocolentry::getNumber() const {
84 	return (pvt->_pe)?pvt->_pe->p_proto:-1;
85 }
86 
needsMutex()87 bool protocolentry::needsMutex() {
88 	#if !defined(RUDIMENTS_HAVE_GETPROTOBYNAME_R) || \
89 		!defined(RUDIMENTS_HAVE_GETPROTOBYNUMBER_R)
90 		return true;
91 	#else
92 		return false;
93 	#endif
94 }
95 
setMutex(threadmutex * mtx)96 void protocolentry::setMutex(threadmutex *mtx) {
97 	#if !defined(RUDIMENTS_HAVE_GETPROTOBYNAME_R) || \
98 		!defined(RUDIMENTS_HAVE_GETPROTOBYNUMBER_R)
99 		_pemutex=mtx;
100 	#endif
101 }
102 
initialize(const char * protocolname)103 bool protocolentry::initialize(const char *protocolname) {
104 	return initialize(protocolname,-1);
105 }
106 
initialize(int32_t number)107 bool protocolentry::initialize(int32_t number) {
108 	return initialize(NULL,number);
109 }
110 
initialize(const char * protocolname,int32_t number)111 bool protocolentry::initialize(const char *protocolname, int32_t number) {
112 
113 	pvt->_pe=NULL;
114 	if (!protocolname && number==-1) {
115 		return false;
116 	}
117 
118 	#if defined(RUDIMENTS_HAVE_GETPROTOBYNAME_R) && \
119 		defined(RUDIMENTS_HAVE_GETPROTOBYNUMBER_R)
120 		delete[] pvt->_buffer;
121 		pvt->_buffer=NULL;
122 		// getprotobyname_r is goofy.
123 		// It will retrieve an arbitrarily large amount of data, but
124 		// requires that you pass it a pre-allocated buffer.  If the
125 		// buffer is too small, it returns an ENOMEM and you have to
126 		// just make the buffer bigger and try again.
127 		for (int32_t size=1024; size<MAXBUFFER; size=size+1024) {
128 			pvt->_buffer=new char[size];
129 			#if defined(RUDIMENTS_HAVE_GETPROTOBYNAME_R_5) && \
130 				defined(RUDIMENTS_HAVE_GETPROTOBYNUMBER_R_5)
131 			if (!((protocolname)
132 				?(getprotobyname_r(protocolname,
133 							&pvt->_pebuffer,
134 							pvt->_buffer,size,
135 							&pvt->_pe))
136 				:(getprotobynumber_r(number,
137 							&pvt->_pebuffer,
138 							pvt->_buffer,size,
139 							&pvt->_pe)))) {
140 				return (pvt->_pe!=NULL);
141 			}
142 			#elif defined(RUDIMENTS_HAVE_GETPROTOBYNAME_R_4) && \
143 				defined(RUDIMENTS_HAVE_GETPROTOBYNUMBER_R_4)
144 			if ((protocolname)
145 				?(pvt->_pe=getprotobyname_r(protocolname,
146 							&pvt->_pebuffer,
147 							pvt->_buffer,size))
148 				:(pvt->_pe=getprotobynumber_r(number,
149 							&pvt->_pebuffer,
150 							pvt->_buffer,size))) {
151 				return true;
152 			}
153 			#endif
154 			delete[] pvt->_buffer;
155 			pvt->_buffer=NULL;
156 			pvt->_pe=NULL;
157 			if (error::getErrorNumber()!=ENOMEM) {
158 				return false;
159 			}
160 		}
161 		return false;
162 	#else
163 		return (!(_pemutex && !_pemutex->lock())) &&
164 			((pvt->_pe=((protocolname)
165 				?getprotobyname(protocolname)
166 				:getprotobynumber(number)))!=NULL) &&
167 			!(_pemutex && !_pemutex->unlock());
168 	#endif
169 }
170 
getNumber(const char * protocolname)171 int32_t protocolentry::getNumber(const char *protocolname) {
172 	protocolentry	pe;
173 	return (pe.initialize(protocolname))?pe.getNumber():-1;
174 }
175 
getName(int32_t number)176 char *protocolentry::getName(int32_t number) {
177 	protocolentry	pe;
178 	return (pe.initialize(number))?
179 			charstring::duplicate(pe.getName()):NULL;
180 }
181