AMI test client for VoLTE development

This commit is contained in:
Andreas Eversberg 2024-05-07 12:40:57 +02:00
parent 4cd2c70ee9
commit cedd34759a
308 changed files with 39639 additions and 0 deletions

1
ami_test_client/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
ami_test_client

339
ami_test_client/LICENSE Normal file
View File

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

20
ami_test_client/Makefile Normal file
View File

@ -0,0 +1,20 @@
CC = gcc
CFLAGS = -Wall -ggdb -Ilibc-jss
LDFLAGS = -lev -lm -lcrypto
OBJ=ami.o originate.o libc-jss/netsocket.o libc-jss/logger.o libc-jss/misc.o milenage.o
PROGS=ami_test_client
.PHONY: all
all: $(patsubst %, %.o, $(PROGS)) $(OBJ) $(PROGS)
%: %.o $(OBJ)
gcc $(CFLAGS) -o $@ $< $(OBJ) $(LDFLAGS)
clean:
rm -f *.o $(OBJ) $(PROGS)
volcon.o: volcon.c volcon_config.h
install: compile
install -s volcon /usr/local/bin

69
ami_test_client/NOTES.txt Normal file
View File

@ -0,0 +1,69 @@
BUGOK:
- Asterisk 1.4 esetén az originate.c rossz Hangup-cause kódot ad vissza, ha Zap
csatornán indítjuk a hívást. Rendre 19-et ad vissza. Nem az originate.c
hibája, mert az AMI dumpban is 19 látszódik. Local csatornán rendben működik.
TODO:
-első körben tesztelni kéne az ami.c-t a test_ami.c -vel. Írni valami kis célprogramot! pl. mellék állapot listázás
-ami.c-ben ami_event_list_t átnevezése valami kultúráltabb típusnévre, mert ez az API-ban megjelenik
-időzítőt betenni, hogy ha megszakad a kapcsolat az Asteriskkel, akkor ping timeout legyen
-időzítőt betenni, ami törlődik a sikeres authentikációnál
-Asterisk Call Manager/1.1 fejléc megérkezése előtt senki nem írhat az AMI socketre, mert ez lesz:
Asterisk Call Manager/1.1
Response: Error
ActionID: 1
Message: Missing action in request
-CLI response és az olyan több csomagos válaszok, mint pl. a DongleShowDevices kezelése
-ami_action-t ne lehessen regisztrálni, amíg nincs connected állapot
(vigyázni a beépített authentication ami_action-re!)
-ami_connect legyen hatástlan, ha már épp kapcsolódik vagy kapcsolódva van
-ami_event_unregister megírása
-ami dump events: response kulonvalasztasa
-event es response kulonvalasztasa keresesnel
-megvizsgálni, hogy az originate.c egyszerűbben működhet -e a linkedid változóval?
-Asterisk 1.4-et dobni, mert nem kell!
CLI formátum:
action: command
command: sip show domains
Response: Follows
Privilege: Command
SIP Domain support not enabled.
--END COMMAND--
SMS küldése
action: DongleSendPDU
ActionID: 59
Device: dongle0
PDU: 0031000B816002660304F000000010C8329BFD061DA74DD0F52D679343
Válasz a várakozósorból
Response: Success
ActionID: 59
Message: [dongle0] SMS queued for send
ID: 0x5e13c0
Elküldve
Event: DongleSMSStatus
Privilege: call,all
Device: dongle0
ID: 0x5e13c0
Status: Sent
Nincs elküldve
Event: DongleSMSStatus
Privilege: call,all
Device: dongle0
ID: 0x5c0888
Status: NotSent
JEGYZETEK
=========
A linkedid változó, pl: ${CHANNEL(LinkedID)} hívásonként és nem csatornánként
változik. Tehát a tovább kapcsolt, illetve Local channel által továbbvitt
hívások ugyan azt a LinkedID-t kapják.

76
ami_test_client/README.md Normal file
View File

@ -0,0 +1,76 @@
# Asterisk Manager Interface client C library
libamievent is an asynchronous event-driven client library for Asterisk Manager
Interface written in C. It uses [libev](http://software.schmorp.de/pkg/libev.html) as event
loop backend.
With the libamievent you can send AMI commands and you can subscribe for
response to the command. When it arrives, the libamievent call the callback
function, which specified at subscription. The callback function allows you to
query AMI variables.
libamievent support AMI events. You can also specify a callback function of
what the libamievent is called when events are received.
Huge advantage of the libamievent, that the AMI commands and event names are
not hardcoded in the library. All commands, event names, parameters and
variables must be defined by printf-style strings with variable substitution.
## Requirements
* libev
For Debian users:
apt-get install libev-dev
For Gentoo users:
emerge -av libev
## Install
Currently, the Makefile is not prepared to carry out normal library. But, you
can build the example codes.
Clone the libamievent repo with all submodules.
git clone --recursive git://github.com/andrewjsi/libamievent
Compile the source, test and install:
make
## Using library
Sorry, the documentation still needs work, but in the meantime check out the sample
programs.
## Todo, Future
* more stability
* more examples
* build static and dynamic library
* well-written documentation
* the ability to be integrated with other systems as easy as possible
## Source code
The source code is well written as far as possible. We do not use tabs, instead
of 4 spaces is indented. All identifiers, including variables, function names
and macros written in English, but some comments and commits in Hungarian is,
because we are speaking and thinking in Hungarian. Nevertheless, we try to
write everything in English in the future.
## Contribution
It is an open source project, which is to be better and better. If you have any
ideas or find an error, or get stuck, you can contact us, please file a bug
report or send a pull-request!
## License
[_GPL2_](https://www.gnu.org/licenses/gpl-2.0.html)
(C) Copyright 2010-2014 Andras Jeszenszky, [JSS & Hayer
IT](http://www.jsshayer.hu) All Rights Reserved.

864
ami_test_client/ami.c Normal file
View File

@ -0,0 +1,864 @@
/* ami.c
* Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
// debug infók (kommentezd, ha nem kell)
// #define CON_DEBUG
// csomagok dumpolása stdout-ra (kommentezd, ha nem kell)
// #define AMI_DEBUG_PACKET_DUMP
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <ev.h>
#include <stdio.h> // TODO: kell ez?
#include <stdarg.h>
#include <sys/time.h> // gettimeofday()
#include "ami.h"
#include "debug.h"
#include "utlist.h"
#include "misc.h"
#include "logger.h"
// event rögzítése
void put_event (ami_event_t *event) {
ami_event_t *event_copy = (ami_event_t*)malloc(sizeof(ami_event_t));
if (!event_copy) {
conft("can't allocate event: %s, event lost", strerror(errno));
return;
}
//~ event tartalmának másolása az új event_copy számára lefoglalt területre.
//~ Emiatt lesz thread-safe a várakozó sor és a callback hívás.
memcpy(event_copy, event, sizeof(ami_event_t));
// bedobjuk a listába az új eseményt
DL_APPEND(event->ami->event_head, event_copy);
//~ Esedékessé tesszük az azonnali (0 sec) időzítőt, aminek hatására az event
//~ loop a callback futtatások után azonnal meghívja az invoke_events()
//~ függvényt, ami meghivogatja a sorban álló eventekhez tartozó callback
//~ eljárásokat. Majd a multithread fejlesztésénél ezen a ponton az
//~ ev_timer_start() helyett az ev_async() függvénnyel kell jelezni a másik
//~ szálban futó event loop-nak, hogy dolog van.
ev_timer_start(event->ami->loop, &event->ami->need_event_processing);
}
/* Felépítjük az event->field string tömböt, amiben az Asterisk által
küldött "változó: érték" párokat mentjük el úgy, hogy "változó", "érték",
"változó", "érték", ... A tömböt az ami->inbuf mutatókkal való
feldarabolásával és NULL byteok elhelyezésével kapjuk.
A sorvégeket lezárhatja \r\n és \n is egyaránt.
A legutolsó sor végét nem kötelező lezárni.
Ha az ami->inbuf tartalma:
Response: Success
Message: Authentication accepted
Akkor az ami->field:
{"Respone","Success","Message","Authentication accepted"}
field string tömb
max_field_size maximum ennyi darab string rakható a field tömbbe
field_len annyi lesz az értéke, ahány elem bekerült a field tömbbe
data innen olvassuk az adatokat és ezt a buffert daraboljuk fel és zárjuk le NULL-al
data_size data mérete
*/
//~ TODO: Hibás a függvény működése, ha a **field tömbünk mérete kicsi és a
//~ feldarabolás során nem férnek el benne a tokenek. Nincs segfault meg
//~ memóriahiba, hanem csak annyi történik, hogy az utolsó változó-érték pár
//~ értéke megkapja sortörésekkel együtt a maradék buffert. Ezt úgy lehetne
//~ megoldani, hogy a függvény nem bal-jobb oldalt vizsgál, hanem egy for ciklus
//~ NULL-ra állítja a ": " és a "\r" és "\n" karaktereket a teljes data-ban, majd
//~ csak ezután következne a feldarabolás mutatókkal.
//~
//~ aug 21: A fent leirt hiba sz'tem nem hiba.
//~
//~ aug 29: mi lenne, ha az utolsó változó-érték pár nem kapná meg a teljes buffert?
//~ vagy eleve netsocket_disconnect és feltakarítás kellene ide?
//~
//~ A függvény nem kezeli azt az esetet, amikor az AMI változó-érték párnak nincs
//~ értéke és nincs a változó utáni kettőspont után szóköz, hanem egyből újsor
//~ karakter. Ilyen eset áll fenn az RTCPSent esemény ReportBlock változójában.
void tokenize_field (int *field, int max_field_size, int *field_len, char *data, int data_size) {
enum {
LEFT,
RIGHT,
} inexpr = LEFT;
int len = 0; // visszatéréskor ezt mentjük el a *field_len -be
field[len++] = 0; // első pozíció a data legeleje, tehát 0
int i;
// összes \r karakter nullázása
for (i = 0; i < data_size; i++)
if (data[i] == '\r')
data[i] = '\0';
for (i = 0; i < data_size && len < max_field_size; i++) {
if (inexpr == LEFT) { // ": " bal oldalán vagyunk, változó
if (data[i] == ':' && data[i+1] == ' ') {
data[i] = '\0';
data[i+1] = '\0';
i += 2;
field[len++] = i;
inexpr = RIGHT;
}
}
if (inexpr == RIGHT) { // ": " jobb oldalán vagyunk, érték
if (data[i] == '\n') {
data[i] = '\0';
i += 1;
field[len++] = i;
inexpr = LEFT;
}
}
}
if (inexpr == LEFT)
len--;
*field_len = len;
// AMI bal és jobb értékek dumpolása
#ifdef AMI_DEBUG_PACKET_DUMP
int z;
for (z = 0; z < len; z++)
printf("tokenize_field ### %d - (%s)\n", z, &data[field[z]]);
printf("\n");
#endif
}
static char *type2name (enum ami_event_type type) {
switch (type) {
case AMI_EVENT: return "EVENT" ; break;
case AMI_RESPONSE: return "RESPONSE" ; break;
case AMI_CLIRESPONSE: return "CLIRESPONSE" ; break;
case AMI_CONNECT: return "CONNECT" ; break;
case AMI_DISCONNECT: return "DISCONNECT" ; break;
default: return "UNKNOWN";
}
}
// belső esemény kiváltása (AMI_CONNECT, AMI_DISCONNECT, stb...)
static void generate_local_event (ami_t *ami, enum ami_event_type type, const char *fmt, ...) {
ami_event_t event_tmp; // ideiglenes event // TODO: ha működik, akkor bevezetni az ami->event_tmp helyett lent is
ami_event_t *event = &event_tmp;
bzero(event, sizeof(*event));
//~ char buf[AMI_BUFSIZ];
va_list ap;
va_start(ap, fmt);
vsnprintf(event->data, sizeof(event->data), fmt, ap);
va_end(ap);
event->data[AMI_BUFSIZ-1] = '\0'; // védelem // TODO: kell ez?
event->data_size = strlen(event->data);
//~ printf("~~~ %s ~~~\n", event->data);
tokenize_field(
event->field,
sizeof(event->field) / sizeof(char*) - 1,
&event->field_size,
event->data,
event->data_size
);
ami_event_list_t *el = NULL;
// végigmegyünk a regisztrált eseményeken
DL_FOREACH(ami->ami_event_list_head, el) {
if (el->type == type) {
event->callback = el->callback;
event->userdata = el->userdata;
event->regby_file = el->regby_file;
event->regby_line = el->regby_line;
event->regby_function = el->regby_function;
event->regby_cbname = el->regby_cbname;
event->regby_udname = el->regby_udname;
event->ami = ami;
event->type = el->type;
put_event(event);
}
}
}
static void parse_cliresponse (ami_t *ami, int actionid, char *buf, int size) {
printf("***** CLI RESPONSE START *****\n");
printf("***** ActionID = %d\n", actionid);
int i;
for (i = 0; i < size; i++) {
putchar(buf[i]);
}
printf("***** CLI RESPONSE END *****\n\n");
}
// bejövő Response es Event feldolgozása
static void parse_input (ami_t *ami, char *buf, int size) {
ami_event_t *event = &ami->event_tmp;
bzero(event, sizeof(*event));
memcpy(event->data, buf, size);
event->data_size = size;
tokenize_field(
event->field,
sizeof(event->field) / sizeof(char*) - 1,
&event->field_size,
event->data,
size
);
char *var_response = ami_getvar(event, "Response");
char *var_event = ami_getvar(event, "Event");
/* * * RESPONSE * * */
if (!strlen(var_event)) {
char *action_id_str = ami_getvar(event, "ActionID");
if (action_id_str == NULL) {
con_debug("Missing ActionID in Response!");
return;
}
event->action_id = atoi(action_id_str);
if (!strcmp(var_response, "Success")) {
event->success = 1;
} else if (!strcmp(var_response, "Error")) {
event->success = 0;
} else {
con_debug("Unknown Response value: %s", var_response);
return;
}
con_debug("RESPONSE - success = %d, action_id = %d", event->success, event->action_id);
ami_event_list_t *el = NULL;
ami_event_list_t *eltmp = NULL;
DL_FOREACH_SAFE(ami->ami_event_list_head, el, eltmp) {
if (el->type != AMI_RESPONSE) // csak az AMI_RESPONSE típusú eseményeket vizsgáljuk
continue;
// event->action_id - Asterisktől érkezett ActionID
// el->action_id - adatbázisban szereplő ActionID
if (event->action_id == el->action_id) {
event->callback = el->callback;
event->userdata = el->userdata;
event->regby_file = el->regby_file;
event->regby_line = el->regby_line;
event->regby_function = el->regby_function;
event->regby_cbname = el->regby_cbname;
event->regby_udname = el->regby_udname;
event->ami = ami;
event->type = AMI_RESPONSE;
put_event(event);
DL_DELETE(ami->ami_event_list_head, el);
free(el);
return;
}
}
con_debug("Received ActionID=%d, but %d not found in ami_event_list_head!", event->action_id, event->action_id);
/* * * EVENT * * */
} else {
//~ printf("##### PARSE_INPUT EVENT #####\n");
ami_event_list_t *el;
// végigmegyünk a regisztrált eseményeken
DL_FOREACH(ami->ami_event_list_head, el) {
if (el->type != AMI_EVENT) // csak az AMI_EVENT típusú eseményeket vizsgáljuk
continue;
// regisztrációban definiált változó=érték párok száma
int need_found = el->field_size / 2; // minden találatnál dekrementálva lesz
//~ printf(" REG need_found=%d allevents=%d by %s:%d\n", need_found, el->allevents, el->regby_file, el->regby_line);
if (need_found || el->allevents) { // ha van mit keresnünk
int n, i;
// végigmegyünk a regisztráció változó=érték párjain
for (n = 0; n < el->field_size; n += 2) {
//~ printf(" _reg_ %s=%s\n", &el->data[el->field[n]], &el->data[el->field[n+1]]);
// végigmegyünk a bejövő csomag változó=érték párjain
for (i = 0; i < event->field_size; i += 2) {
//~ printf(" _eve_ %s=%s\n", &event->data[event->field[i]], &event->data[event->field[i+1]]);
// ha egyezik a regisztrált változó neve a csomag változó nevével
if (!strcmp(&el->data[el->field[n]], &event->data[event->field[i]])) {
// ha egyezik a regisztrált változó értéke a csomag változó értékével
if (!strcmp(&el->data[el->field[n+1]], &event->data[event->field[i+1]])) {
//~ printf(" !found\n");
need_found--;
}
}
}
}
//~ printf(" FIN need_found=%d\n", need_found);
// ha minden változó megtalálható volt és mindegyik értéke egyezett
// vagy "*" volt megadva a regisztrációnál (allevents)
if (need_found == 0 || el->allevents) {
event->callback = el->callback;
event->userdata = el->userdata;
event->regby_file = el->regby_file;
event->regby_line = el->regby_line;
event->regby_function = el->regby_function;
event->regby_cbname = el->regby_cbname;
event->regby_udname = el->regby_udname;
event->type = AMI_EVENT;
event->ami = ami;
put_event(event);
}
}
}
}
}
void ami_disconnect (ami_t *ami, const char *fmt, ...) {
char reason_text[AMI_BUFSIZ];
va_list ap;
va_start(ap, fmt);
vsnprintf(reason_text, sizeof(reason_text), fmt, ap);
va_end(ap);
if (netsocket_is_connected(ami->netsocket))
netsocket_disconnect_withevent(ami->netsocket, reason_text);
}
static void response_login (ami_event_t *response) {
ami_t *ami = response->ami;
con_debug("auth reply: success=%d %s (by %s() %s:%d)",
response->success,
ami_getvar(response, "Message"),
response->regby_function,
response->regby_file,
response->regby_line
);
if (!response->ami->authenticated) {
if (response->success) { // AUTH accepted
response->ami->authenticated = 1;
// TODO: itt kell a connect timeout időzítőt törölni
generate_local_event(ami,
AMI_CONNECT,
"Host: %s\nIP: %s\nPort: %d",
ami->host,
ami->netsocket->ip,
ami->port);
} else { // AUTH failed
netsocket_disconnect_withevent(response->ami->netsocket, "Authentication failed");
}
}
}
static void process_input (ami_t *ami) {
#ifdef AMI_DEBUG_PACKET_DUMP
int pdi;
printf("----- NETSOCKET INBUF START -----\n");
for (pdi = 0; pdi < ami->netsocket->inbuf_len; pdi++)
putchar(ami->netsocket->inbuf[pdi]);
printf("----- NETSOCKET INBUF END -----\n");
#endif
/*
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
E v e n t : D i a l \r \n \r \n A c t i o n
\r \n \r \n
"Response: Follows\r\nPrivilege: Command\r\nActionID: %d\r\n"
"--END COMMAND--\r\n\r\n"
*/
// byte-onként végigmegyünk a netsocket bufferen
int i;
for (i = 0; i < ami->netsocket->inbuf_len; i++) {
// buffer overflow védelem
if (ami->inbuf_pos >= sizeof(ami->inbuf) - 1) {
printf("ELFOGYOTT A BUFFER!!!\n"); // TODO: netsocket_disconnect és feltakarítás
break;
}
// byte másolása netsocketből ami->inbuf -ba
ami->inbuf[ami->inbuf_pos] = ami->netsocket->inbuf[i];
/* AMI header vizsgalata. Biztonsagi okokbol ha mar authentikalt
allapotban vagyunk, akkor ezt a vizsgalatot kihagyjuk */
// TODO. lekezelni azt az esetet, amikor kezdésnek nem ezt a fejlécet kapjuk!
if (!ami->authenticated &&
(!strcmp(ami->inbuf, "Asterisk Call Manager/1.1\r\n") ||
!strcmp(ami->inbuf, "Asterisk Call Manager/1.0\r\n") ||
!strcmp(ami->inbuf, "Asterisk Call Manager/1.2\r\n") ||
!strcmp(ami->inbuf, "Asterisk Call Manager/1.3\r\n")))
{
bzero(ami->inbuf, sizeof(ami->inbuf));
ami->inbuf_pos = 0;
con_debug("Received \"Asterisk Call Manager\" header, sending auth...");
ami_action(ami, response_login, NULL,
"Action: Login\nUsername: %s\nSecret: %s\n",
ami->username, ami->secret);
bzero(ami->inbuf, sizeof(ami->inbuf));
ami->inbuf_pos = 0;
return;
}
// ha épp egy "Response: Follows" belsejében vagyunk
if (ami->cli_actionid > 0) {
// keressük a csomag végét
#define Q "--END COMMAND--\r\n\r\n"
#define QSIZE 19
if (ami->inbuf_pos >= QSIZE - 1) {
if (!strncmp(Q, &ami->inbuf[ami->inbuf_pos - (QSIZE - 1)], QSIZE)) {
// megtaláltuk, mehet a feldolgozóba
parse_cliresponse(ami, ami->cli_actionid, ami->inbuf, ami->inbuf_pos - 18);
bzero(ami->inbuf, sizeof(ami->inbuf));
ami->inbuf_pos = 0;
ami->cli_actionid = 0;
continue;
}
}
#undef Q
#undef QSIZE
// folytatjuk tovább a beolvasást
ami->inbuf_pos++;
continue;
}
/* Egy "Action: Command" csomagra (ami_cli() okozza) egy "Response:
Follows" válaszcsomag érkezik. Az ilyen csomagoknak speciális
formátuma van, ezért ezeket teljesen külön kell kezelni. Ezek a
csomagok a parse_input() helyett a parse_cliresponse() függvénynek
kerülnek át feldolgozásra. Az alábbi sscanf() megoldás megvizsgálja,
hogy az éppen beolvasás alatt álló csomag ilyen speciális "Response:
Follows" csomag lesz -e, illetve kiszedi belőle az ActionID-t. A
sscanf() az ami->inbuf baloldali illeszkedését vizsgálja és ha
tudja, akkor az ami->cli_actionid változóba menti el a kapott
ActionID-t. */
if (sscanf(ami->inbuf, "Response: Follows\r\nPrivilege: Command\r\nActionID: %d\r\n", &ami->cli_actionid) > 0) {
// az ami->inbuf jobboldali illeszkedését az strncmp()-vel vizsgáljuk
#define Q "\r\n"
#define QSIZE 2 // Q mérete
if (ami->inbuf_pos >= QSIZE - 1) {
// ha illeszkedik jobbról a \r\n, akkor tovább olvassuk az adatokat
if (!strncmp(Q, &ami->inbuf[ami->inbuf_pos - (QSIZE - 1)], QSIZE)) {
bzero(ami->inbuf, sizeof(ami->inbuf));
ami->inbuf_pos = 0;
continue; // ezen a ponton az ami->cli_actionid -ben ott figyel az ActionID
}
}
#undef Q
#undef QSIZE
/* Ide akkor kerülünk, ha a sscanf() (bal oldal) illeszkedett, de az
strncmp() (jobb oldal) nem. Ebben az esetben elképzelhető, hogy
a sscanf() hibásan kiolvassa az ActionID egy töredékét, ezért
biztos ami biztos, kinullázzuk. ezen a ponton lehetséges, hogy a
sscanf() az ActinID csak egy töredékét szedte ki, ezért nullázunk. */
ami->cli_actionid = 0;
// folytatjuk tovább a beolvasást
ami->inbuf_pos++;
continue;
}
/* Ha van elég adat, hogy az ami->inbuf -ban 3 byte-ot visszaléphessünk,
akkor megvizsgáljuk, hogy vajon éppen egy csomag lezárásánál állunk
-e, azaz az ami->inbuf legutolsó 4 byte-ja megegyezik ezzel:
"\r\n\r\n". Ha igen, akkor az azt jelenti, hogy az ami->inbuf pont
egy teljes csomagot tartalmaz, amit elküldünk a parse_input()-nak,
majd kinullázzuk a teljes ami->inbuf buffert és az ami->inbuf_pos
pozicionáló változót. Ezután folytatjuk a következő csomag byte-
onkénti olvasását. Ha már nincs a netsocket->inbuf -ban feldolgozandó
cucc, akkor a for ciklus kilép és majd a következő körben
folytatódik az olvasás. */
/* TODO: Logikailag nem korrekt ez a megoldás! Segfault veszély
ami_event_unregister() után. A process_input() és parse_input()
páros a teljes netsocket->inbuf -ban lévő cuccot egyetlen egy körben
feldolgozza. Ebben egyszerre több csomag is lehet. Az érdekes
eseményeket a put_event() a saját listájába tolja és csak a
következő körben lesz callback hívás. Tegyük fel, hogy egyszerre 2
csomag érkezik. Az első csomagban lévő esemény callback függvénye
megrendel egy új eseményt, amire történetesen pont a második csomag
illeszkedne. De mivel a megrendelés előtt már megtörtént az
összehasonlítás, szűrés és a futtatandó események kiválasztása,
ezért erről a második eseményről le fog maradni a hívó. Másképpen
szólva egy-egy megrendelésnek (vagy lemondásnak) csak a teljes
put_event() által karbantartott lista (callback lista) lefuttatása
után lesz hatása. Ez eseményről való lemaradást okozhat, illetve
lemondásnál segfaultot is, ugyanis ha történik egy
ami_event_unregister() akkor ezután még a put_event() által
karbantartott listából lefuthat a (már lemondott) callback. Ötlet:
valami olyan megoldás kéne, hogy még itt a process_input /
parse_input szintjén ha fennakad a szűrőn egy esemény, akkor a
put_event() regisztráció után álljon le a parse_input() és az event
loop hívja meg a need_event_processing-et. És majd csak ezután
folytatódjon a parse_input() vizsgálódása. Vaaagy... egy merészebb
ötlet. A bejövő AMI buffer visszamenőleg addig legyen eltárolva,
amíg az invoke_events még foglalkozik a callback hívásokkal.
Multithread környezetben az invoke_events a megrendelő szálában fog
futni. Elképzelhető, hogy az invoke_events-ből kellene vizsgálni
azt, hogy az éppen bejövő AMI eseményt kell -e futtatni. */
#define Q "\r\n\r\n"
#define QSIZE 4
if (ami->inbuf_pos >= QSIZE - 1) {
if (!strncmp(Q, &ami->inbuf[ami->inbuf_pos - (QSIZE - 1)], QSIZE)) {
parse_input(ami, ami->inbuf, ami->inbuf_pos - 1); // -1 azért, hogy ne menjen át a legutolsó \r\n-ből az \r (érthető?)
bzero(ami->inbuf, sizeof(ami->inbuf));
ami->inbuf_pos = 0;
continue; // ne jusson el az ami->inbuf_pos++ -ig :)
}
}
#undef Q
#undef QSIZE
ami->inbuf_pos++;
}
}
static void netsocket_callback (netsocket_t *netsocket, int event) {
ami_t *ami = netsocket->userdata;
int was_authenticated = 0;
switch (event) {
case NETSOCKET_EVENT_CONNECT:
con_debug("Connected to %s (%s) port %d",
netsocket->host,
netsocket->ip,
netsocket->port
);
break;
case NETSOCKET_EVENT_DISCONNECT:
was_authenticated = ami->authenticated;
// TODO: itt kell alaphelyzetbe állítani az ami-t.
// disconnect esemény szétkűrtölése előtt
ami->authenticated = 0;
generate_local_event(ami,
AMI_DISCONNECT,
"Host: %s\nIP: %s\nPort: %d\nReason: %s\nWasAuthenticated: %d",
netsocket->host,
(netsocket->ip) ? netsocket->ip : "",
netsocket->port,
netsocket->disconnect_reason,
was_authenticated
);
if (netsocket->connected) {
con_debug("Disconnected from %s: %s",
netsocket->host,
netsocket->disconnect_reason
);
} else {
con_debug("Can't connect to %s[%s]:%d %s",
netsocket->host,
(netsocket->ip) ? netsocket->ip : "",
netsocket->port,
netsocket->disconnect_reason
);
}
break;
case NETSOCKET_EVENT_READ:
process_input(ami);
break;
}
}
// hívja az ami->need_event_processing azonnali időzítő
static void invoke_events (EV_P_ ev_io *w, int revents) {
ami_t *ami = w->data;
ami_event_t *event, *tmp;
DL_FOREACH_SAFE(ami->event_head, event, tmp) {
if (event->callback != NULL) {
con_debug("call %s()", event->regby_cbname);
event->callback(event);
con_debug("end %s()", event->regby_cbname);
}
DL_DELETE(ami->event_head, event);
free(event);
}
}
// 6 byte-os random hexa stringet masol az ami->uuid bufferbe
// TODO: egy rendes, unique ID-t visszaado fuggvenyt irni ehelyett a random vacak helyett
// pl. az util-linux-ng csomagban levo libuuid segitsegevel
static void generate_uuid (char *dst, size_t size) {
struct timeval tv;
int num;
char tmp[16];
gettimeofday(&tv, NULL);
srand(tv.tv_usec * tv.tv_sec);
num = rand();
snprintf(tmp, sizeof(tmp), "%x", num);
tmp[6] = '\0';
strncpy(dst, tmp, size);
}
void connect_delayed (EV_P_ ev_io *w, int revents) {
ami_t *ami = w->data;
con_debug("invoked connect by timer");
ev_timer_stop(ami->loop, &ami->t_connect_delayed);
ami_connect(ami);
}
// delay: millisec
void ami_connect_delayed (ami_t *ami, int delay) {
con_debug("connect after %d ms ...", delay);
ev_timer_stop(ami->loop, &ami->t_connect_delayed);
ev_timer_set(&ami->t_connect_delayed, (float)((float)delay / (float)1000), 0);
ev_timer_start(ami->loop, &ami->t_connect_delayed);
}
ami_t *ami_new (struct ev_loop *loop) {
ami_t *ami = malloc(sizeof(*ami));
if (ami == NULL) {
con_debug("ami_new() returned NULL");
return NULL;
}
bzero(ami, sizeof(*ami)); // minden NULL
// AMI UUID
generate_uuid(ami->uuid, sizeof(ami->uuid));
// ha meg van adva a loop paraméter, akkor azt használjuk eseménykezelőnek
// ellenkező esetben az alapértelmezett eseménykezelőt
ami->loop = (loop != NULL) ? loop : ev_default_loop(0);
// default értékek
strncpy(ami->host, AMI_DEFAULT_HOST, sizeof(ami->host) - 1);
ami->port = AMI_DEFAULT_PORT;
if (!(ami->netsocket = netsocket_new(netsocket_callback, ami, ami->loop))) {
con_debug("netsocket_new returned NULL");
}
netsocket_host(ami->netsocket, AMI_DEFAULT_HOST);
netsocket_port(ami->netsocket, AMI_DEFAULT_PORT);
ami->need_event_processing.data = ami; // ami objektum így kerül az invoke_events-be
ev_timer_init(&ami->need_event_processing, (void*)invoke_events, 0, 0);
ami->t_connect_delayed.data = ami;
ev_timer_init(&ami->t_connect_delayed, (void*)connect_delayed, 0, 0);
return ami;
}
void ami_destroy(ami_t *ami) {
netsocket_destroy(ami->netsocket);
}
void ami_credentials (ami_t *ami, const char *username, const char *secret, const char *host, const char *port) {
if (username != NULL)
strncpy(ami->username, username, sizeof(ami->username) - 1);
if (secret != NULL)
strncpy(ami->secret, secret, sizeof(ami->secret) - 1);
if (host != NULL)
strncpy(ami->host, host, sizeof(ami->host) - 1);
if (port != NULL) {
int port_tmp = atoi(port);
if (port_tmp > 0 || port_tmp < 65536)
ami->port = port_tmp;
}
}
void ami_connect (ami_t *ami) {
netsocket_host(ami->netsocket, ami->host);
netsocket_port(ami->netsocket, ami->port);
netsocket_connect(ami->netsocket);
}
int ami_printf (ami_t *ami, const char *fmt, ...) {
char buf[AMI_BUFSIZ];
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
int field[AMI_FIELD_SIZE];
int field_size;
tokenize_field(
field,
sizeof(field) / sizeof(char*) - 1,
&field_size,
buf,
strlen(buf)
);
char packet[AMI_BUFSIZ];
int i;
strcpy(packet, "");
for (i = 0; i < field_size; i += 2)
concatf(packet, "%s: %s\r\n", &buf[field[i]], &buf[field[i+1]]);
concat(packet, "\r\n");
if (ami->netsocket != NULL) {
#ifdef AMI_DEBUG_PACKET_DUMP
printf("----- NETSOCKET WRITE START ------\n");
printf("%s", packet);
printf("----- NETSOCKET WRITE END ------\n");
#endif
return netsocket_printf(ami->netsocket, "%s", packet);
} else {
return -1;
}
}
ami_event_list_t *_ami_action (ami_t *ami, void *callback, void *userdata, char *file, int line, const char *function, const char *cbname, const char *udname, const char *fmt, ...) {
char buf[AMI_BUFSIZ];
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
buf[AMI_BUFSIZ-1] = '\0'; // védelem
if (callback != NULL) {
ami_event_list_t *el = malloc(sizeof(ami_event_list_t));
bzero(el, sizeof(*el)); // NULL, NULL, NULL :)
el->callback = callback;
el->userdata = userdata;
el->type = AMI_RESPONSE;
el->regby_file = file;
el->regby_line = line;
el->regby_function = function;
el->regby_cbname = cbname;
el->regby_udname = udname;
ami->action_id++; // új ActionID
el->action_id = ami->action_id;
ami_printf(ami, "ActionID: %d\n%s", ami->action_id, buf);
con_debug("registered action #%d, callback: %s()", el->action_id, el->regby_cbname);
DL_APPEND(ami->ami_event_list_head, el);
return el;
} else {
ami_printf(ami, "%s", buf);
return NULL;
}
}
ami_event_list_t *_ami_event_register (ami_t *ami, void *callback, void *userdata, char *file, int line, const char *function, const char *cbname, const char *udname, const char *fmt, ...) {
ami_event_list_t *el = malloc(sizeof(ami_event_list_t));
bzero(el, sizeof(*el)); // NULL, NULL, NULL :)
va_list ap;
va_start(ap, fmt);
vsnprintf(el->data, sizeof(el->data), fmt, ap);
va_end(ap);
el->callback = callback;
el->userdata = userdata;
el->regby_file = file;
el->regby_line = line;
el->regby_function = function;
el->regby_cbname = cbname;
el->regby_udname = udname;
// belső esemény: Connect
if (!strcmp(el->data, "Connect")) {
el->type = AMI_CONNECT;
// belső esemény: Disconnect
} else if (!strcmp(el->data, "Disconnect")) {
el->type = AMI_DISCONNECT;
// Minden Asterisk esemény szűrés nélkül
} else if (!strcmp(el->data, "*")) {
el->type = AMI_EVENT;
el->allevents = 1;
// Asterisk esemény, feltételek feldarabolása
} else {
el->type = AMI_EVENT;
tokenize_field(
el->field,
sizeof(el->field) / sizeof(char*) - 1,
&el->field_size,
el->data,
sizeof(el->data)
);
}
DL_APPEND(ami->ami_event_list_head, el);
con_debug("EVENT registered, callback: %s by %s() in %s line %d",
el->regby_cbname, el->regby_function, el->regby_file, el->regby_line);
return el;
}
void ami_event_unregister(ami_t *ami, ami_event_list_t *el) {
if (el == NULL) {
con_debug("attempting to unregister NULL pointer event!");
return;
}
con_debug("EVENT unregistered, callback: %s()", el->regby_cbname);
DL_DELETE(ami->ami_event_list_head, el);
free(el);
}
void ami_event_dump (ami_event_t *event) {
printf(
"Incoming %s /0x%lx/\n"
" Registered by %s() in %s line %d\n"
" Callback: %s() /0x%lx/, Userdata: %s /0x%lx/\n"
" success=%d action_id=%d data_size=%d field_size=%d\n"
, type2name(event->type), (unsigned long)event
, event->regby_function, event->regby_file, event->regby_line
, event->regby_cbname, (unsigned long)event->callback, event->regby_udname, (unsigned long)event->userdata
, event->success, event->action_id, event->data_size, event->field_size
);
int i;
for (i = 0; i < event->field_size; i += 2)
printf(" %-16s %s\n", &event->data[event->field[i]], &event->data[event->field[i+1]]);
printf("\n");
}
void ami_dump_event_list_element (ami_event_list_t *el) {
printf(
"Registered %s /0x%lx/\n"
" Registered by %s() in %s line %d\n"
" Callback: %s() /0x%lx/, Userdata: %s /0x%lx/\n"
" action_id=%d field_size=%d\n"
, type2name(el->type), (unsigned long)el
, el->regby_function, el->regby_file, el->regby_line
, el->regby_cbname, (unsigned long)el->callback, el->regby_udname, (unsigned long)el->userdata
, el->action_id, el->field_size
);
int i;
for (i = 0; i < el->field_size; i += 2)
printf(" %-16s %s\n", &el->data[el->field[i]], &el->data[el->field[i+1]]);
printf("\n");
}
void ami_dump_lists (ami_t *ami) {
printf("** REGISTERED AMI EVENTS **\n");
ami_event_list_t *el;
DL_FOREACH(ami->ami_event_list_head, el)
ami_dump_event_list_element(el);
}
char *ami_getvar (ami_event_t *event, char *var) {
int i;
for (i = 0; i < event->field_size; i += 2) {
if (!strcmp(&event->data[event->field[i]], var)) {
if (&event->data[event->field[i+1]] != NULL) {
return &event->data[event->field[i+1]];
} else {
return "";
}
}
}
return "";
}
void ami_strncpy (ami_event_t *event, char *dest, char *var, size_t maxsize) {
}

135
ami_test_client/ami.h Normal file
View File

@ -0,0 +1,135 @@
/* ami.h
* Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
#ifndef AMI_H_INCLUDED
#define AMI_H_INCLUDED
#include "netsocket.h"
// TODO: doksiba AMI_DEBUG_PACKET_DUMP
#ifndef AMI_DEFAULT_HOST
#define AMI_DEFAULT_HOST "localhost"
#endif
#ifndef AMI_DEFAULT_PORT
#define AMI_DEFAULT_PORT 5038
#endif
#ifndef AMI_BUFSIZ
#define AMI_BUFSIZ 4096
#endif
#ifndef AMI_FIELD_SIZE
#define AMI_FIELD_SIZE 300
#endif
enum ami_event_type {
AMI_EVENT = 1,
AMI_RESPONSE,
AMI_CLIRESPONSE,
AMI_CONNECT,
AMI_DISCONNECT,
};
// ha változik, akkor egyeztess az ami.c ami_dump_lists() függvénnyel!
typedef struct ami_event_list_t {
struct ami_event_list_t *prev;
struct ami_event_list_t *next;
void (*callback)(void*);
void *userdata;
int field[AMI_FIELD_SIZE];
int field_size;
char data[AMI_BUFSIZ];
char *regby_file;
int regby_line;
const char *regby_function;
const char *regby_cbname;
const char *regby_udname;
unsigned int action_id;
enum ami_event_type type;
int allevents; // 1 lesz, ha a megrendelo szures nelkul az osszes AMI esemenyt keri
} ami_event_list_t;
typedef struct ami_event_t {
struct ami_event_t *prev;
struct ami_event_t *next;
struct ami_t *ami;
int success; // csak "Response: Success" esetén lesz egy, tehát biztos hogy volt Response és az értéke Success volt
int field[AMI_FIELD_SIZE];
int field_size;
char data[AMI_BUFSIZ];
int data_size;
void (*callback)(void*);
void *userdata;
unsigned int action_id;
char *regby_file;
int regby_line;
const char *regby_function;
const char *regby_cbname;
const char *regby_udname;
enum ami_event_type type;
} ami_event_t;
typedef struct ami_t {
char host[64]; // Asterisk host
int port; // Asterisk Manager Interface port
char username[32]; // AMI User
char secret[32]; // AMI Password
netsocket_t *netsocket; // Netsocket objektum
char disconnect_reason[64]; // ???
ami_event_list_t *ami_event_list_head; // megrendelt események
struct ev_loop *loop; // eseményhurok
ev_timer need_event_processing; // azonnali időzítő az események kiküldéséhez
ev_timer t_connect_delayed; // késleltetett connect időzítő
char inbuf[AMI_BUFSIZ]; // bejövő buffer
int inbuf_pos; // bejövő buffer pozíciója
struct ami_event_t *event_head; // esemény várakozósor
struct ami_event_t event_tmp; // itt készítünk új eseményt, amit aztán a várakozósorba másolunk
int authenticated; // 1 lesz sikeres login után
unsigned int action_id; // soron következő használható ActionID
char uuid[16]; // AMI sajat belso ID
int cli_actionid; // process_input() itt jegyzi meg a Response: Follows ActionID-t
} ami_t;
ami_t *ami_new (struct ev_loop *loop);
void ami_credentials (ami_t *ami, const char *username, const char *secret, const char *host, const char *port);
void ami_destroy(ami_t *ami);
void ami_connect (ami_t *ami);
void ami_connect_delayed (ami_t *ami, int delay);
int ami_printf (ami_t *ami, const char *fmt, ...);
#define ami_action(ami, callback, userdata, ...) \
_ami_action(ami, callback, userdata, __FILE__, __LINE__, __FUNCTION__, #callback, #userdata, __VA_ARGS__)
ami_event_list_t *_ami_action (ami_t *ami, void *callback, void *userdata, char *file, int line, const char *function, const char *cbname, const char *udname, const char *fmt, ...);
#define ami_event_register(ami, callback, userdata, ...) \
_ami_event_register(ami, callback, userdata, __FILE__, __LINE__, __FUNCTION__, #callback, #userdata, __VA_ARGS__)
ami_event_list_t *_ami_event_register (ami_t *ami, void *callback, void *userdata, char *file, int line, const char *function, const char *cbname, const char *udname, const char *fmt, ...);
void ami_event_unregister(ami_t *ami, ami_event_list_t *el);
char *ami_getvar (ami_event_t *event, char *var);
#define ami_strcpy(event,dest,var) ami_strncpy(event,dest,var,sizeof(dest))
void ami_strncpy (ami_event_t *event, char *dest, char *var, size_t maxsize);
void ami_dump_lists (ami_t *ami);
void ami_event_dump (ami_event_t *el);
void ami_disconnect (ami_t *ami, const char *fmt, ...);
#endif // #ifndef AMI_H_INCLUDED

View File

@ -0,0 +1,279 @@
#include <stdio.h>
#include <string.h>
#include <ev.h>
#include <stdlib.h>
#include <errno.h>
#include "ami.h"
#include "debug.h"
#include "utlist.h"
#define CON_DEBUG
#include "logger.h"
#include "milenage.h"
ev_timer timer;
ev_timer timer_reconnect;
ami_t *ami;
int dstatus_n = 2;
//~ static void ami_event_callback (ami_event_t *ame) {
//~ char *userdata = (char*)ame->userdata;
//~ char *status = ami_getvar(ame, "Status");
//~
//~ char status2[64];
//~ strncpy(status2, ami_getvar(ame, "Status"), 64);
//~ ami_strncpy(ame, status2, "Status", 64);
//~
//~ ami_event_unregister(ame);
//~ }
void utproba () {
typedef struct st {
struct st *prev;
struct st *next;
char str[32];
} st;
st *head = NULL;
st *el;
st *x10, *x15;
int i;
for (i = 0; i < 20; i++) {
el = (st*)malloc(sizeof(*el));
bzero(el, sizeof(*el));
sprintf(el->str, "str %d", i);
if (i == 10)
x10 = el;
if (i == 15)
x15 = el;
DL_APPEND(head, el);
}
st *eltmp;
DL_FOREACH_SAFE(head, el, eltmp) {
if (el == x10) {
st *csere = (st*)malloc(sizeof(st));
strcpy(csere->str, "csere1");
DL_APPEND(head, csere);
csere = (st*)malloc(sizeof(st));
strcpy(csere->str, "csere2");
DL_APPEND(head, csere);
DL_DELETE(head, el);
free(el);
}
if (el == x15) {
DL_DELETE(head, el);
free(el);
}
}
DL_FOREACH_SAFE(head, el, eltmp) {
printf("s %s s\n", el->str);
DL_DELETE(head, el);
free(el);
}
return;
}
int do_register = 0;
void event_response (ami_event_t *event) {
const char *message_str = ami_getvar(event, "Message");
printf("response: %s\n", message_str);
}
void event_fullybooted (ami_event_t *event) {
printf("*** Fully booted ***\n");
if (do_register)
return;
do_register = 1;
ami_action(ami, event_response, NULL, "Action: PJSIPRegister\nRegistration: volte_ims");
}
int hex_to_octet_string(const char *name, const char *input, uint8_t *output, size_t output_size)
{
int i, n;
if (!input || !input[0]) {
printf("Missing value for hex string '%s'.\n", name);
return -1;
}
i = n = 0;
while (*input) {
if (i == output_size) {
printf("Value for hex string '%s' too long, expecting %zu bytes.\n", name,
output_size);
return -1;
}
if (*input >= '0' && *input <= '9')
output[i] = (output[i] << 4) | (*input - '0');
else if (*input >= 'a' && *input <= 'f')
output[i] = (output[i] << 4) | (*input - 'a' + 10);
else if (*input >= 'A' && *input <= 'F')
output[i] = (output[i] << 4) | (*input - 'A' + 10);
else {
printf("Value for hex string '%s' has invalid character '%c'.\n", name, *input);
return -1;
}
if (++n == 2) {
n = 0;
i++;
}
input++;
}
if (i < output_size) {
printf("Value for hex string '%s' too short, expecting %zu bytes.\n", name, output_size);
return -1;
}
return 0;
}
void octest_string_to_hex(uint8_t *input, size_t input_size, char *output)
{
while(input_size) {
sprintf(output, "%02x", *input);
input++;
input_size--;
output += 2;
}
}
static const char *usim_opc = "775A1F887D2AD66F9719C2C79F847B50";
static const char *usim_k = "D534E07854B75E475C667A856AA31F9C";
static const char *usim_sqn = "000000011000";
void event_authrequest (ami_event_t *event) {
const char *rand_str, *autn_str;
uint8_t opc[16], k[16], sqn[6], rand[16], autn[16], res[8], ik[16], ck[16], auts[14];
char res_str[256], ik_str[256], ck_str[256], auts_str[256];
size_t res_len = 8;
rand_str = ami_getvar(event, "RAND");
autn_str = ami_getvar(event, "AUTN");
int rc;
if (!rand_str || !autn_str) {
printf("Missing parameters in AuthRequest\n");
return;
}
if (hex_to_octet_string("RAND", rand_str, rand, sizeof(rand))) {
printf("Invalid RAND parameter in AuthRequest\n");
return;
}
if (hex_to_octet_string("AUTN", autn_str, autn, sizeof(autn))) {
printf("Invalid AUTN parameter in AuthRequest\n");
return;
}
if (hex_to_octet_string("OPC", usim_opc, opc, sizeof(opc))) {
printf("Invalid OPC parameter in AuthRequest\n");
return;
}
if (hex_to_octet_string("K", usim_k, k, sizeof(k))) {
printf("Invalid K parameter in AuthRequest\n");
return;
}
if (hex_to_octet_string("SQN", usim_sqn, sqn, sizeof(sqn))) {
printf("Invalid SQN parameter in AuthRequest\n");
return;
}
rc = milenage_check(opc, k, sqn, rand, autn, ik, ck, res, &res_len, auts);
octest_string_to_hex(res, sizeof(res), res_str);
octest_string_to_hex(ik, sizeof(ik), ik_str);
octest_string_to_hex(ck, sizeof(ck), ck_str);
octest_string_to_hex(auts, sizeof(auts), auts_str);
if (rc == -2) {
printf("*** Sending AuthResponse with authentication token ***\n");
ami_action(ami, NULL, NULL, "Action: AuthResponse\nRegistration: volte_ims\nAUTS: %s", auts_str);
} else if (!rc) {
printf("*** Sending AuthResponse with authentication result and keys ***\n");
ami_action(ami, NULL, NULL, "Action: AuthResponse\nRegistration: volte_ims\nRES: %s\nCK: %s\nIK: %s", res_str, ck_str, ik_str);
} else {
printf("*** Sending AuthResponse with authentication error\n");
ami_action(ami, NULL, NULL, "Action: AuthResponse\nRegistration: volte_ims");
}
}
void reconnect_ami (ami_event_t *event) {
printf("*** Reconnecting... ***\n");
ev_timer_stop(EV_DEFAULT, &timer_reconnect);
ami_connect(ami);
}
void event_connect (ami_event_t *event) {
printf("*** CONNECTED ***\n");
printf(" semmi = %s\n host = %s\n ip = %s\n port = %s\n",
ami_getvar(event, "semmi"),
ami_getvar(event, "Host"),
ami_getvar(event, "IP"),
ami_getvar(event, "Port"));
}
void event_disconnect (ami_event_t *event) {
printf("*** DISCONNECTED ***\n");
printf(" reason = %s\n host = %s\n ip = %s\n port = %s\n",
ami_getvar(event, "Reason"),
ami_getvar(event, "Host"),
ami_getvar(event, "IP"),
ami_getvar(event, "Port"));
ev_timer_again(EV_DEFAULT, &timer_reconnect);
ev_timer_start(EV_DEFAULT, &timer_reconnect);
}
int main (int argc, char *argv[]) {
ami = ami_new(EV_DEFAULT);
if (ami == NULL) {
con_debug("ami_new() returned NULL");
return 1;
}
char host[128];
if (argc < 2) {
//~ printf("usage: %s <host>\n", argv[0]);
printf("Default host used!\n");
strcpy(host, "127.0.0.1");
} else {
strcpy(host, argv[1]);
}
printf("Connecting to %s ...\n", host);
ami_credentials(ami, "jolly", "svenja", host, "5038");
ami_connect(ami);
ami_event_register(ami, event_fullybooted, NULL, "Event: FullyBooted");
ev_timer_init(&timer_reconnect, (void*)reconnect_ami, 3, 3);
//~ ev_timer_start(EV_DEFAULT, &timer_reconnect);
ami_event_register(ami, event_connect, NULL, "Connect");
ami_event_register(ami, event_disconnect, NULL, "Disconnect");
ami_event_register(ami, event_authrequest, NULL, "Event: AuthRequest");
printf("\n");
ami_dump_lists(ami);
ami_action(ami, NULL, NULL, "Action: Login\nUsername: jolly\nSecret: svenja");
ev_loop(EV_DEFAULT, 0);
ami_destroy(ami);
return 0;
}

View File

@ -0,0 +1,30 @@
2013-09-17 - netsocket
==========
A netsocket mostantól nem fogad el kívülről értékeket, tehát nem szabad
közvetlenül a programból manipulálni a netsocket_t objektumot. A host, lhost,
port, lport értékeket az alábbi függvények segítségével lehet beállítani. Ennek
oka az, hogy eddig a netsocket_t mutatókban tárolta a host és lhost stringeket,
ami azt okozta, hogy ha a főprogramban megváltoztattuk a hostot, akkor a
netsocket-ben is megváltozott, hisz ugyan arra mutattak. Mostantól a
netsocket_t saját char[] tömbben tárolja ezeket az értékeket, így nem lesz
hatással semmilyen külső változtatás, illetve nem fog összekeveredni.
Eddig így kellett hívni:
netsocket->host = host;
netsocket->port = port;
netsocket->lhost = lhost;
netsocket->lport = lport;
Mostantól pedig így kell:
netsocket_host(netsocket, host);
netsocket_port(netsocket, port);
netsocket_lhost(netsocket, lhost);
netsocket_lport(netsocket, lport);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@ -0,0 +1,29 @@
CC = gcc
CFLAGS = -Wall
LIBS = -lcrypto -lev
OBJ=pipe.o netsocket.o
BIN=pipe
all: logger pipe fmtsub_test
pipe: $(OBJ)
$(CC) $(CFLAGS) -o $(BIN) $(OBJ) $(EXTOBJ) $(LIBS)
logger: logger.o logger_test.o misc.o
$(CC) $(CFLAGS) -o logger logger.o logger_test.o misc.o
fmtsub_test: misc.o fmtsub_test.o
$(CC) $(CFLAGS) -o fmtsub_test misc.o fmtsub_test.o
doc:
doxygen
@echo ""
@echo "DOC URL: file://"`pwd`"/html/index.html"
@echo ""
clean:
rm -rf $(OBJ) $(BIN) logger fmtsub_test core *.o html/ latex/ man/
# Függőségek
#policy.o: server.h

View File

@ -0,0 +1,15 @@
Ring Bufferrel megoldhato a mem logging?
http://en.wikipedia.org/wiki/Circular_buffer
LIBEV STRICT ALIASING
=====================
netsocket.c -ben a libev örjöng:
* libjsi/netsocket.c: In function netsocket_connect:
* libjsi/netsocket.c:264: warning: dereferencing type-punned pointer will break strict-aliasing rules
A megoldás egyelőre az, hogy -fno-strict-aliasing CFLAGS-et használjuk.

View File

@ -0,0 +1,28 @@
# JSS C Library
This library contains the C functions and macros, which are often used in our
projects. In the future, this will be a stand-alone library with unit tests and
documentation.
[AMISMS](https://github.com/andrewjsi/amisms) and [libamievent](https://github.com/andrewjsi/libamievent) uses this library.
## Source code
The source code is well written as far as possible. We do not use tabs, instead
of 4 spaces is indented. All identifiers, including variables, function names
and macros written in English, but some comments and commits in Hungarian is,
because we are speaking and thinking in Hungarian. Nevertheless, we try to
write everything in English in the future.
## Contribution
It is an open source project, which is to be better and better. If you have any
ideas or find an error, or get stuck, you can contact us, please file a bug
report or send a pull-request!
## License
[_GPL2_](https://www.gnu.org/licenses/gpl-2.0.html)
(C) Copyright 2010-2014 Andras Jeszenszky, [JSS & Hayer
IT](http://www.jsshayer.hu) All Rights Reserved.

View File

@ -0,0 +1,19 @@
CC = gcc
CFLAGS = -Wall -ggdb -I..
CFLAGS += -DCON_DEBUG
LIBS = -lev -lm -lmysqlclient -lpthread
OBJ=amysql.o ../logger.o ../misc.o
all: test_amysql test_sync_amysql
test_amysql: test_amysql.o $(OBJ)
$(CC) $(CFLAGS) -o test_amysql test_amysql.o $(OBJ) $(LIBS)
test_sync_amysql: test_sync_amysql.o $(OBJ)
$(CC) $(CFLAGS) -o test_sync_amysql test_sync_amysql.o $(OBJ) $(LIBS)
clean:
rm -f $(OBJ) *.o test_amysql test_sync_amysql

View File

@ -0,0 +1,30 @@
libzdb - http://www.tildeslash.com/libzdb/
A small, easy to use Open Source Database Connection Pool Library with the
following features:
-Thread safe Database Connection Pool
-Connect to multiple database systems
-Zero runtime configuration, connect using a URL scheme
-Supports MySQL, PostgreSQL, SQLite and Oracle
MySQL C Library API Reference:
http://dev.mysql.com/doc/refman/5.1/en/c-api-functions.html
MySQL C API Tutorial:
http://zetcode.com/tutorials/mysqlcapitutorial/
MySQL szerverben trigger - adding a New User-Defined Function
http://dev.mysql.com/doc/refman/5.5/en/adding-udf.html
http://jan.kneschke.de/2008/9/9/async-mysql-queries-with-c-api/
tömör leírás az async elvről:
http://betterlogic.com/roger/2008/08/gdb-says-asynchronous-mysql-c-api/
http://stackoverflow.com/questions/11313686/using-libmysqlclient-in-multi-threaded-application
http://www.linuxtopia.org/online_books/database_guides/mysql_5.1_database_reference_guide/threaded-clients.html
http://dev.mysql.com/doc/refman/5.1/en/threaded-clients.html

View File

@ -0,0 +1,321 @@
/* amysql.c
* Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/* TODO: megvizsgálni a libzdb-t, mert ismeri a MySQL-t, PostgreSQL-t és az
Oracle-t is. A libzdb tudtommal nem aszinkron, viszont az amysql.c mintájára
aszinkronná lehetne tenni:) */
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <mysql/mysql.h>
#include <ev.h>
#include "debug.h"
#include "logger.h"
#define FIELDBUFSIZE 8192
#define FIELDVSIZE 32
#define QUERYBUFSIZE 4096
MYSQL *db;
MYSQL_RES *result;
MYSQL_ROW row;
int busy = 0;
void (*callback)(int, char**, char*);
int fieldc; // result mezők száma
char *fieldv[FIELDVSIZE]; // result mező elemek
char fieldbuf[FIELDBUFSIZE]; // result buffer (ide mutatnak a fieldv elemek)
char querybuf[QUERYBUFSIZE]; // itt tároljuk el a query-t
char error_str[256];
int error_num;
struct option_t {
char host[64];
char user[64];
char password[64];
char database[64];
char charset[16];
int connect_timeout;
int data_timeout;
} option = {
.host = "localhost",
.user = "",
.password = "",
.database = "",
.charset = "", // alapértelmezetten nem piszkáljuk a charset-et
.connect_timeout = 10, // TODO: prod környezetben ezeket magasra venni!
.data_timeout = 20, /* Each attempt uses this timeout value and there
are retries if necessary, so the total effective
timeout value is three times the option value. */
};
static ev_async evasync_main;
static ev_async evasync_thread;
static struct ev_loop *loop_main; // főprogram event loopja (kapja az amysql_init())
static struct ev_loop *loop_thread; // thread event loopja
pthread_t thread_id;
void amysql_option_host (const char *host) {
strncpy(option.host, host, sizeof(option.host) - 1);
}
void amysql_option_user (const char *user) {
strncpy(option.user, user, sizeof(option.user) - 1);
}
void amysql_option_password (const char *password) {
strncpy(option.password, password, sizeof(option.password) - 1);
}
void amysql_option_database (const char *database) {
strncpy(option.database, database, sizeof(option.database) - 1);
}
void amysql_option_charset (const char *charset) {
strncpy(option.charset, charset, sizeof(option.charset) - 1);
}
void amysql_option_connect_timeout (int connect_timeout) {
option.connect_timeout = connect_timeout;
}
void amysql_option_data_timeout (int data_timeout) {
option.data_timeout = data_timeout;
}
// thread: AMYSQL, ha amysql_query() hívás volt
// thread: nincs definiálva, ha amysql_sync_query() hívás volt
// Függvény a következőket csinálja:
// reset, init, config, connect, query, fetch, save, disconnect, free
static void query () {
con_debug("send query: %s", querybuf);
//** Buffer reset **//
memset(fieldbuf, 0, sizeof(fieldbuf));
memset(fieldv, 0, sizeof(fieldv));
memset(error_str, 0, sizeof(error_str));
fieldc = 0;
db = NULL;
result = NULL;
//** Init **//
db = mysql_init(NULL);
if (db == NULL) {
con_debug("mysql_init() returned NULL");
char *errmsg = "mysql_init(): Can't allocate memory";
strncpy(error_str, errmsg, sizeof(error_str));
error_num = -1;
goto done;
}
//** Config **//
mysql_options(db, MYSQL_OPT_CONNECT_TIMEOUT, &option.connect_timeout);
mysql_options(db, MYSQL_OPT_READ_TIMEOUT, &option.data_timeout);
mysql_options(db, MYSQL_OPT_WRITE_TIMEOUT, &option.data_timeout);
//** Connect **//
if (!mysql_real_connect(db, option.host, option.user, option.password, option.database, 0, NULL, 0)) {
con_debug("Can't connect to MySQL server: %s", mysql_error(db));
strncpy(error_str, mysql_error(db), sizeof(error_str));
error_num = -1;
goto done;
}
if (option.charset && strlen(option.charset)) {
if (mysql_set_character_set(db, option.charset)) {
strncpy(error_str, mysql_error(db), sizeof(error_str));
error_num = -1;
goto done;
}
}
//** Query **//
if (mysql_query(db, querybuf)) {;
con_debug("query failed: %s", mysql_error(db));
strncpy(error_str, mysql_error(db), sizeof(error_str));
error_num = -1;
goto done;
}
//** Result **//
result = mysql_store_result(db);
if (result == NULL) { // INSERT INTO sikeres
con_debug("INSERT or UPDATE OK: result = NULL");
error_num = 0;
goto done;
}
//** Fetch only ONE row **//
int num_fields = mysql_num_fields(result);
row = mysql_fetch_row(result);
if (row == NULL) { // SELECT-ben a WHERE nem illeszkedett semmire
con_debug("SELECT empty: row = NULL");
error_num = 0;
goto done;
}
/* Az alábbi for ciklus a következőket csinálja: Egyetlen row mezőinek
másolása fieldbuf bufferbe és feldarabolása fieldv-be. A fieldbuf-ba
folyamatosan bemásoljuk a row-ból kiolvasott byteokat. Ezzel a
folytonossággal helyet spórolunk meg, mert nem kell minden mezőnek fix
méretet lefoglalni. A fieldv mutató tömbbe pedig az egyes mezők
kezdőcimeit mentjük el, ami a fieldbuf azon indexére mutat, ahol az
adott mező kezdődik. Az argc és *argv[] pároshoz hasonlóan a fieldc
változóba kerül a fieldv tömb mérete. Ha elegendő volt a FIELDBUFSIZE és
a FIELDVSIZE, akkor ez megegyezik a MySQL által visszaadott
mysql_num_fields() értékével. Ellenkező esetben annyi lesz, amennyit
sikerült kigyűjteni, tehát amennyit biztonsággal ki lehet olvasni. */
//** Save **//
int rpos = 0;
for (fieldc = 0; fieldc < num_fields && fieldc < FIELDVSIZE; fieldc++) {
if (row[fieldc] == NULL) {
fieldv[fieldc] = NULL;
} else {
fieldv[fieldc] = &fieldbuf[rpos];
int i;
for (i = 0; row[fieldc][i] != 0; i++) {
fieldbuf[rpos] = row[fieldc][i];
rpos++;
if (rpos > FIELDBUFSIZE - 2) { // ne szaladjunk túl a fieldbuf méretén
fieldc++;
error_num = 0;
goto done;
}
}
fieldbuf[rpos] = 0;
rpos++;
if (rpos > FIELDBUFSIZE - 2) { // ne szaladjunk túl a fieldbuf méretén
fieldc++;
error_num = 0;
goto done;
}
}
}
//~ int k; for (k = 0; k < fieldc; k++) printf("%d s %s s\n", k, fieldv[k]); printf("\n");
done:
//** Free & Disconnect **//
if (result != NULL)
mysql_free_result(result);
if (db != NULL)
mysql_close(db);
// TODO: strerror üzenetét hozzácsapni az errstr-hez, az err != 0
// ha 4-es hiba (interrupted system call) jön, akkor azt a mysql_connect()
// timeout okozza. Vajon ez hogy van megoldva? SIGALRM??? Az bebaszna!
//perror("strerror()");
}
void query_and_evasync () {
query();
con_debug("ev_async: AMYSQL -> MAIN");
ev_async_send(loop_main, &evasync_main);
}
// thread: MAIN
int amysql_is_busy () {
return busy;
}
// thread: MAIN
int amysql_query (void *cb, const char *fmt, ...) {
if (busy) {
con_debug("amysql is BUSY");
return -1;
}
busy = 1;
va_list ap;
va_start(ap, fmt);
vsnprintf(querybuf, sizeof(querybuf) - 1, fmt, ap);
va_end(ap);
callback = cb;
con_debug("ev_async: MAIN -> AMYSQL");
ev_async_send(loop_thread, &evasync_thread);
return 0;
}
// thread: MAIN
static void invoke_callback (EV_P_ ev_async *w, int revents) {
/* callback hívás közben már lehet új amysql_query() függvényt hívni,
mert féligmeddig biztonságos. Lehetőség szerint a callback függvény
legvégén legyen új amysql_query() hívás, mert az amysql thread felül
fogja írni a fieldc/fieldv és error_num/error_str változókat. */
busy = 0;
callback(fieldc, fieldv, (error_num) ? error_str : NULL);
}
// thread: AMYSQL
static void *infinite_loop () {
/* Itt lehet inicializálni az AMYSQL thread saját event watchereit */
ev_loop(loop_thread, 0);
con_debug("FATAL: amysql event loop exited! ");
return NULL;
}
// thread: MAIN
int amysql_init (struct ev_loop *loop) {
busy = 0;
loop_main = loop;
loop_thread = ev_loop_new(EVFLAG_AUTO);
ev_async_init(&evasync_main, (void*)invoke_callback);
ev_async_init(&evasync_thread, (void*)query_and_evasync);
/* A mostani kezdetleges thread tapasztalataim alapján, szerintem az
ev_async watchereket itt, még a pthread_create() előtt kell indítani.
Azaz még a hívó thread kontextusában. Mert ha az új thread-be
indítanánk, akkor elképzelhető, hogy visszatérés után a hívó függvény
egyből egy ev_async_send() hívással folytatja a futást és lehet, hogy az
új thread még nem járna az ev_async_init()-nél és ev_async_start()-nál.
Ez pedig ugye nem , mert azelőtt hívnánk meg valamit, mint hogy
inicializáltuk volna. A többi, (a thread-re nézve saját) event watcher,
io, időzítő, stb.. inicializálását már végezheti a thread saját maga,
tehát mehet a pthread_create() által meghívott függvénybe. */
ev_async_start(loop_main, &evasync_main);
ev_async_start(loop_thread, &evasync_thread);
int err;
err = pthread_create(&thread_id, NULL, infinite_loop, NULL);
if (err)
con_debug("thread creating error: (%d) %s", err, strerror(err));
return err;
}
int amysql_sync_query (int *parc, char **parv[], const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vsnprintf(querybuf, sizeof(querybuf) - 1, fmt, ap);
va_end(ap);
query();
if (parc)
*parc = fieldc;
if (parv)
*parv = fieldv;
return error_num;
}
const char *amysql_strerror () {
return (error_num) ? error_str : NULL;
}

View File

@ -0,0 +1,55 @@
/* amysql.h
* Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
#include <ev.h>
/* Azért kell a loop paraméter, mert innen tudja az amysql, hogy melyik
event loop-ban kell majd meghívni a callback-et.*/
int amysql_init (struct ev_loop *loop);
/* Az amysql_query() azonnal visszaadja a vezérlést, és a háttérben egy
másik szálon megkezdi a paraméterben megadott lekérdezést az adatbázis
szerver felé. Amikor megjön a válasz, akkor lezárja a szerverrel a
kapcsolatot és meghívja a paraméterben megadott callback függvényt.
Figyelem! A callback függvény végén, de tényleg a legvégén lehetőség van új
amysql_query() függvény hívására. Hatására a fieldc/fieldv és a hibaváltozók
törlődni fognak, amiket a callback függvény is kapott.*/
int amysql_query (void *cb, const char *fmt, ...);
/* Konfigurációs függvények */
// default: localhost
void amysql_option_host (const char *host);
void amysql_option_user (const char *user);
void amysql_option_password (const char *password);
void amysql_option_database (const char *database);
// default: nem hívja meg a mysql_set_character_set() függvényt
void amysql_option_charset (const char *charset);
// default: 10
void amysql_option_connect_timeout (int connect_timeout);
// default: 20
void amysql_option_data_timeout (int data_timeout);
// visszaadja a hiba szövegét vagy NULL-t, ha nem volt hiba
const char *amysql_strerror ();
// nem-aszinkron lekérdezés. A lekérdezés erejéig az amysql_sync_query() hívás
// blokkol. Az eredményt a parc és a parv-be menti el.
int amysql_sync_query (int *parc, char **parv[], const char *fmt, ...);

View File

@ -0,0 +1,57 @@
/* test_amysql.c
* Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ev.h>
#include "amysql.h"
#include "debug.h"
static struct ev_loop *loop;
static ev_timer timeout_watcher;
static void cbtest (int fc, char *fv[], char *e);
static void q () {
amysql_query(cbtest, "select * from smsout where sent='0' order by RAND()");
}
static void cbtest (int fc, char *fv[], char *e) {
debi(fc);
printf("e=%s\n", e);
int i;
for (i = 0; i < fc; i++) {
printf("%d --- %s\n", i, fv[i]);
}
// 3 másodperc múlva q() futtatása
ev_once(loop, -1, -1, 3, q, 0);
}
static void timeout_cb (EV_P_ ev_timer *w, int revents) {
printf("timeout\n");
}
int main (int argc, char *argv[]) {
loop = ev_default_loop (0);
ev_timer_init (&timeout_watcher, timeout_cb, 0, 0.5);
ev_timer_start (loop, &timeout_watcher);
amysql_init(loop);
q(); // berúgjuk a mocit:)
ev_loop (loop, 0);
return 0;
}

View File

@ -0,0 +1,40 @@
/* test_sync_amysql.c
* Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "amysql.h"
#include "debug.h"
int main (int argc, char *argv[]) {
amysql_option_user ("proba");
amysql_option_password ("proba");
amysql_option_database ("proba");
amysql_option_charset ("utf8");
// if (amysql_sync_query(NULL, NULL, "INSERT INTO proba (name, comment) VALUES ('%s', '%s')", "egy", "kettő")) {
// printf("ERROR: %s\n", amysql_strerror());
// }
int parc;
char **parv;
if (amysql_sync_query(&parc, &parv, "select * from proba order by RAND()")) {
printf("ERROR: %s\n", amysql_strerror());
}
int i;
for (i = 0; i < parc; i++) {
printf("%d --- %s\n", i, parv[i]);
}
return 0;
}

View File

@ -0,0 +1,12 @@
/* debug.h
* Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
#define debi(x) printf(#x " = %d\n", ((int)(x)))
#define debf(x) printf(#x " = %f\n", (x))
#define debs(x) printf(#x " = %s\n", (x))

View File

@ -0,0 +1,62 @@
/* fmtsub_test.c
* Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "misc.h"
/*
const char *args[][2] = {
{"%n", "2463434"},
{"%v", "0.9.6-rc2"},
{"%n", "Jekusa Poromeck"},
{0}
};
*/
//~ void prob (const char *args[][2]) {
//~ int i;
//~ for (i = 0; afmt[i][0] != 0; i++) {
//~ printf("hello %s --- %s\n", afmt[i][0], afmt[i][1]);
//~
//~ }
//~ }
int main (int argc, char **argv) {
char buf2[32];
char buf[64];
char *pattern = argv[1];
char proba[64];
strcpy(proba, "PROBA");
const char *args[][2] = {
{"%n", NULL},
{"", "URES_STRING"},
{"%v", "0.9.6-rc2"},
{"%a", "Jekusa Poromeck"},
{"%p", proba},
{"Windows", "Linux"},
{"\\%", "%"},
{0}
};
if (fmtsub(buf, sizeof(buf), pattern, args)) {
printf("fmtsub() hibaval tert vissza!\n");
}
printf("%d s %s s\n", (int)strlen(buf), buf);
strncpy(buf2, buf, sizeof(buf2)-1);
printf("%d s %s s\n", (int)strlen(buf2), buf2);
return 0;
}

View File

@ -0,0 +1,11 @@
CC = gcc
CFLAGS = -Wall -I.. -ggdb
LDFLAGS = -lpthread
all: test_htclient
test_htclient: test_htclient.o htclient.o url_parser.o
$(CC) $(CFLAGS) -o test_htclient test_htclient.o htclient.o url_parser.o $(LDFLAGS)
clean:
rm -f test_htclient *.o

View File

@ -0,0 +1,235 @@
/* htclient.c
* Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdarg.h>
#include "htclient.h"
#include "debug.h"
#include "url_parser.h"
/* void ERR(...)
*
* A paraméterül átadott VA_ARGS formátumú hibaüzenetet bemásolja a req->error
* -ba, majd visszatér return -1 -el. Figyelni kell a blokkos szerkezeteknél:
* Mindenképp használni kell a { } zárójeleket, mivel ez a makró két utasítást
* tartalmaz. A { } elhagyásával logikai hiba keletkezhet a programban.
*/
#define ERR(...) snprintf(htc->error, sizeof(htc->error), __VA_ARGS__); return -1
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
void htclient_dump (htclient_t *htc) {
#define HTCLIENT_DUMPS(s) printf("%13s = %s\n", #s, htc->s)
#define HTCLIENT_DUMPI(i) printf("%13s = %d\n", #i, htc->i)
HTCLIENT_DUMPS(url.full);
HTCLIENT_DUMPS(url.scheme);
HTCLIENT_DUMPS(url.host);
HTCLIENT_DUMPS(url.port);
HTCLIENT_DUMPS(url.path);
HTCLIENT_DUMPS(url.query);
HTCLIENT_DUMPS(url.fragment);
HTCLIENT_DUMPS(url.username);
HTCLIENT_DUMPS(url.password);
HTCLIENT_DUMPS(request_data);
HTCLIENT_DUMPS(response_data);
HTCLIENT_DUMPI(num_headers);
HTCLIENT_DUMPS(header_buf);
HTCLIENT_DUMPS(error);
}
// Skip the characters until one of the delimiters characters found.
// 0-terminate resulting word. Skip the rest of the delimiters if any. Advance
// pointer to buffer to the next word. Return found 0-terminated word.
// (function from Mongoose project)
static char *skip (char **buf, const char *delimiters) {
char *p, *begin_word, *end_word, *end_delimiters;
begin_word = *buf;
end_word = begin_word + strcspn(begin_word, delimiters);
end_delimiters = end_word + strspn(end_word, delimiters);
for (p = end_word; p < end_delimiters; p++) {
*p = '\0';
}
*buf = end_delimiters;
return begin_word;
}
// Parse HTTP headers from the given buffer, advance buffer to the point where
// parsing stopped. (function from Mongoose project)
static void parse_http_headers(char **buf, struct htclient_t *htc) {
size_t i;
for (i = 0; i < ARRAY_SIZE(htc->headers); i++) {
htc->headers[i].name = skip(buf, ": ");
htc->headers[i].value = skip(buf, "\r\n");
if (htc->headers[i].name[0] == '\0')
break;
htc->num_headers = i + 1;
}
}
char *htclient_error (htclient_t *htc) {
if (htc->error == NULL || !strlen(htc->error))
return NULL;
return htc->error;
}
int htclient_url (htclient_t *htc, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vsnprintf(htc->url.full, sizeof(htc->url.full), fmt, ap);
va_end(ap);
struct parsed_url *pu = parse_url(htc->url.full);
if (pu == NULL) {
htc->url.set = 0;
ERR("URL mismatch");
}
htc->url.url_parser_ptr = pu;
htc->url.scheme = pu->scheme;
htc->url.host = pu->host;
htc->url.port = pu->port;
htc->url.path = pu->path;
htc->url.query = pu->query;
htc->url.fragment = pu->fragment;
htc->url.username = pu->username;
htc->url.password = pu->password;
// ha nincs megadva a port, akkor a scheme-ből megállapítjuk
if (!htc->url.port && htc->url.scheme) {
if (!strcmp(htc->url.scheme, "http")) {
// TODO: az strdup() nem okoz memória szivárgást? Ha felszabadítjuk
// az url_parser-t, akkor az felszabadítja ezt is?
htc->url.port = strdup("80");
} else if (!strcmp(htc->url.scheme, "https")) {
htc->url.port = strdup("443");
}
}
// ha valamilyen oknál fogva ezen a ponton nincs
// host vagy nincs port, akkor a további
// grimbuszok elkerülése végett hivát dobunk:)
if (!htc->url.host || !htc->url.port) {
htc->url.set = 0;
ERR("URL mismatch");
}
htc->url.set = 1;
return 0;
}
int htclient_perform (htclient_t *htc) {
struct sockaddr_in serv_addr;
struct hostent *server;
int port;
int sockfd;
int rv;
if (!htc->url.set) {
ERR("URL not set or bogus");
}
port = atoi(htc->url.port);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
ERR("error opening socket: %s", strerror(errno));
}
server = gethostbyname(htc->url.host);
if (server == NULL) {
close(sockfd);
ERR("host not found: %s", htc->url.host);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(port);
if (connect(sockfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) < 0) {
close(sockfd);
ERR("can't connect to %s:%d: %s", htc->url.host, port, strerror(errno));
}
rv = write(sockfd, htc->request_data, strlen(htc->request_data));
if (rv < 0) {
close(sockfd);
ERR("ERROR writing to socket");
}
memset(htc->response_data, 0, sizeof(htc->response_data));
rv = read(sockfd, htc->response_data, sizeof(htc->response_data) - 1);
if (rv < 0) {
close(sockfd);
ERR("ERROR reading from socket");
}
htc->header_buf = strdup(htc->response_data);
char *buf = htc->header_buf;
parse_http_headers(&buf, htc);
close(sockfd);
return 0;
}
char *htclient_header_get (htclient_t *htc, const char *name) {
int i;
for (i = 0; i < htc->num_headers; i++) {
if (htc->headers[i].name == NULL || htc->headers[i].value == NULL)
continue;
if (!strcmp(htc->headers[i].name, name))
return htc->headers[i].value;
}
return NULL;
}
htclient_t *htclient_new () {
htclient_t *htc = malloc(sizeof(*htc));
if (htc == NULL) {
fprintf(stderr, "out of memory");
return NULL;
}
memset(htc, 0, sizeof(*htc));
return htc;
}
void htclient_request_set (htclient_t *htc, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vsnprintf(htc->request_data, sizeof(htc->request_data), fmt, ap);
va_end(ap);
}
void htclient_destroy (htclient_t *htc) {
if (htc == NULL)
return;
if (htc->header_buf != NULL) {
free(htc->header_buf);
htc->header_buf = NULL;
}
free(htc);
}

View File

@ -0,0 +1,67 @@
/* htclient.h
* Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
#define HTCLIENT_BUFSIZ 1024
// változás esetén módosítsd a htclient_dump() függvényt a htclient.c fájlban!
typedef struct htclient_t {
struct {
void *url_parser_ptr; // url_parser.h -ban definiált pointer, ezt kell majd felszabadítani
char full[HTCLIENT_BUFSIZ]; // http://jsi@jss.hu/auth?param=59
char *scheme; // http
char *host; // jss.hu
char *port; // (null)
char *path; // auth
char *query; // param=59
char *fragment; // (null)
char *username; // jsi
char *password; // (null)
int set; // 1 lesz, ha rendben beállítottuk az URL-t
} url;
char request_data[HTCLIENT_BUFSIZ];
char response_data[HTCLIENT_BUFSIZ];
struct {
char *name;
char *value;
} headers[16];
int num_headers;
char *header_buf;
char error[128];
} htclient_t;
// új htclient objektum
htclient_t *htclient_new ();
// URL beállítása
// Kezeli a scheme, host, port, path URL részeket. Tovább infó a
// htclient_t->url struktúrában. Az URL path részét nem kezeli a
// htclient_perform(), ezért azt külön el kell menteni a request-ben.
//
// például: http://jss.hu:3333/akarmi.jpg
int htclient_url (htclient_t *htc, const char *fmt, ...);
// Beállítja a teljes HTTP request-et. Beleértve a fejléceket meg mindent.
void htclient_request_set (htclient_t *htc, const char *fmt, ...);
// Végrehajtja a lekérdezést. A függvény addig blokkol, amíg nem érkezik meg a
// válasz vagy valami hiba történik.
int htclient_perform (htclient_t *htc);
// Visszaadja a válaszban szereplő header értékét. Ha a megadott header nem
// létezik, akkor a visszatérési érték NULL.
char *htclient_header_get (htclient_t *htc, const char *name);
// Felszabadítja a htclient objektumot
void htclient_destroy (htclient_t *htc);
// Ha volt hiba, akkor visszaadja a hibastringet, ha nem, akkor NULL
char *htclient_error (htclient_t *htc);
// htclient_t struktúra dumpolása
void htclient_dump (htclient_t *htc);

View File

@ -0,0 +1,66 @@
/* test_htclient.c
* Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include "htclient.h"
#include "debug.h"
#include "url_parser.h"
void url_test () {
htclient_t *h = htclient_new();
htclient_url(h, "http://10.42.20.14:3000/akarmi.jpg");
htclient_dump(h);
htclient_destroy(h);
}
int main (int argc, const char *argv[]) {
// url_test(); return(0);
htclient_t *h;
h = htclient_new();
if (h == NULL) {
printf("out of memory\n");
return -1;
}
htclient_url(h, "http://10.42.20.14:3000/dovecheck");
htclient_request_set(h,
"GET /auth\r\n"
"Auth-server: akarmi\r\n"
"Auth-port: %d\r\n"
"\r\n"
, 5555
);
htclient_perform(h);
htclient_dump(h);
if (htclient_error(h)) {
printf("error: %s\n", htclient_error(h));
goto err;
}
char *hdr = htclient_header_get(h, "Auth-server");
printf("Header vissza: %s\n", hdr);
printf("len = %s\n", htclient_header_get(h, "Content-Length"));
htclient_destroy(h);
return 0;
err:
htclient_destroy(h);
return -1;
}

View File

@ -0,0 +1,329 @@
/*_
* Copyright 2010-2011 Scyphus Solutions Co. Ltd. All rights reserved.
*
* Authors:
* Hirochika Asai
*/
#include "url_parser.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
/*
* Prototype declarations
*/
static __inline__ int _is_scheme_char(int);
/*
* Check whether the character is permitted in scheme string
*/
static __inline__ int
_is_scheme_char(int c)
{
return (!isalpha(c) && '+' != c && '-' != c && '.' != c) ? 0 : 1;
}
/*
* See RFC 1738, 3986
*/
struct parsed_url *
parse_url(const char *url)
{
struct parsed_url *purl;
const char *tmpstr;
const char *curstr;
int len;
int i;
int userpass_flag;
int bracket_flag;
/* Allocate the parsed url storage */
purl = malloc(sizeof(struct parsed_url));
if ( NULL == purl ) {
return NULL;
}
purl->scheme = NULL;
purl->host = NULL;
purl->port = NULL;
purl->path = NULL;
purl->query = NULL;
purl->fragment = NULL;
purl->username = NULL;
purl->password = NULL;
curstr = url;
/*
* <scheme>:<scheme-specific-part>
* <scheme> := [a-z\+\-\.]+
* upper case = lower case for resiliency
*/
/* Read scheme */
tmpstr = strchr(curstr, ':');
if ( NULL == tmpstr ) {
/* Not found the character */
parsed_url_free(purl);
return NULL;
}
/* Get the scheme length */
len = tmpstr - curstr;
/* Check restrictions */
for ( i = 0; i < len; i++ ) {
if ( !_is_scheme_char(curstr[i]) ) {
/* Invalid format */
parsed_url_free(purl);
return NULL;
}
}
/* Copy the scheme to the storage */
purl->scheme = malloc(sizeof(char) * (len + 1));
if ( NULL == purl->scheme ) {
parsed_url_free(purl);
return NULL;
}
(void)strncpy(purl->scheme, curstr, len);
purl->scheme[len] = '\0';
/* Make the character to lower if it is upper case. */
for ( i = 0; i < len; i++ ) {
purl->scheme[i] = tolower(purl->scheme[i]);
}
/* Skip ':' */
tmpstr++;
curstr = tmpstr;
/*
* //<user>:<password>@<host>:<port>/<url-path>
* Any ":", "@" and "/" must be encoded.
*/
/* Eat "//" */
for ( i = 0; i < 2; i++ ) {
if ( '/' != *curstr ) {
parsed_url_free(purl);
return NULL;
}
curstr++;
}
/* Check if the user (and password) are specified. */
userpass_flag = 0;
tmpstr = curstr;
while ( '\0' != *tmpstr ) {
if ( '@' == *tmpstr ) {
/* Username and password are specified */
userpass_flag = 1;
break;
} else if ( '/' == *tmpstr ) {
/* End of <host>:<port> specification */
userpass_flag = 0;
break;
}
tmpstr++;
}
/* User and password specification */
tmpstr = curstr;
if ( userpass_flag ) {
/* Read username */
while ( '\0' != *tmpstr && ':' != *tmpstr && '@' != *tmpstr ) {
tmpstr++;
}
len = tmpstr - curstr;
purl->username = malloc(sizeof(char) * (len + 1));
if ( NULL == purl->username ) {
parsed_url_free(purl);
return NULL;
}
(void)strncpy(purl->username, curstr, len);
purl->username[len] = '\0';
/* Proceed current pointer */
curstr = tmpstr;
if ( ':' == *curstr ) {
/* Skip ':' */
curstr++;
/* Read password */
tmpstr = curstr;
while ( '\0' != *tmpstr && '@' != *tmpstr ) {
tmpstr++;
}
len = tmpstr - curstr;
purl->password = malloc(sizeof(char) * (len + 1));
if ( NULL == purl->password ) {
parsed_url_free(purl);
return NULL;
}
(void)strncpy(purl->password, curstr, len);
purl->password[len] = '\0';
curstr = tmpstr;
}
/* Skip '@' */
if ( '@' != *curstr ) {
parsed_url_free(purl);
return NULL;
}
curstr++;
}
if ( '[' == *curstr ) {
bracket_flag = 1;
} else {
bracket_flag = 0;
}
/* Proceed on by delimiters with reading host */
tmpstr = curstr;
while ( '\0' != *tmpstr ) {
if ( bracket_flag && ']' == *tmpstr ) {
/* End of IPv6 address. */
tmpstr++;
break;
} else if ( !bracket_flag && (':' == *tmpstr || '/' == *tmpstr) ) {
/* Port number is specified. */
break;
}
tmpstr++;
}
len = tmpstr - curstr;
purl->host = malloc(sizeof(char) * (len + 1));
if ( NULL == purl->host || len <= 0 ) {
parsed_url_free(purl);
return NULL;
}
(void)strncpy(purl->host, curstr, len);
purl->host[len] = '\0';
curstr = tmpstr;
/* Is port number specified? */
if ( ':' == *curstr ) {
curstr++;
/* Read port number */
tmpstr = curstr;
while ( '\0' != *tmpstr && '/' != *tmpstr ) {
tmpstr++;
}
len = tmpstr - curstr;
purl->port = malloc(sizeof(char) * (len + 1));
if ( NULL == purl->port ) {
parsed_url_free(purl);
return NULL;
}
(void)strncpy(purl->port, curstr, len);
purl->port[len] = '\0';
curstr = tmpstr;
}
/* End of the string */
if ( '\0' == *curstr ) {
return purl;
}
/* Skip '/' */
if ( '/' != *curstr ) {
parsed_url_free(purl);
return NULL;
}
curstr++;
/* Parse path */
tmpstr = curstr;
while ( '\0' != *tmpstr && '#' != *tmpstr && '?' != *tmpstr ) {
tmpstr++;
}
len = tmpstr - curstr;
purl->path = malloc(sizeof(char) * (len + 1));
if ( NULL == purl->path ) {
parsed_url_free(purl);
return NULL;
}
(void)strncpy(purl->path, curstr, len);
purl->path[len] = '\0';
curstr = tmpstr;
/* Is query specified? */
if ( '?' == *curstr ) {
/* Skip '?' */
curstr++;
/* Read query */
tmpstr = curstr;
while ( '\0' != *tmpstr && '#' != *tmpstr ) {
tmpstr++;
}
len = tmpstr - curstr;
purl->query = malloc(sizeof(char) * (len + 1));
if ( NULL == purl->query ) {
parsed_url_free(purl);
return NULL;
}
(void)strncpy(purl->query, curstr, len);
purl->query[len] = '\0';
curstr = tmpstr;
}
/* Is fragment specified? */
if ( '#' == *curstr ) {
/* Skip '#' */
curstr++;
/* Read fragment */
tmpstr = curstr;
while ( '\0' != *tmpstr ) {
tmpstr++;
}
len = tmpstr - curstr;
purl->fragment = malloc(sizeof(char) * (len + 1));
if ( NULL == purl->fragment ) {
parsed_url_free(purl);
return NULL;
}
(void)strncpy(purl->fragment, curstr, len);
purl->fragment[len] = '\0';
curstr = tmpstr;
}
return purl;
}
/*
* Free memory of parsed url
*/
void
parsed_url_free(struct parsed_url *purl)
{
if ( NULL != purl ) {
if ( NULL != purl->scheme ) {
free(purl->scheme);
}
if ( NULL != purl->host ) {
free(purl->host);
}
if ( NULL != purl->port ) {
free(purl->port);
}
if ( NULL != purl->path ) {
free(purl->path);
}
if ( NULL != purl->query ) {
free(purl->query);
}
if ( NULL != purl->fragment ) {
free(purl->fragment);
}
if ( NULL != purl->username ) {
free(purl->username);
}
if ( NULL != purl->password ) {
free(purl->password);
}
free(purl);
}
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: sw=4 ts=4 fdm=marker
* vim<600: sw=4 ts=4
*/

View File

@ -0,0 +1,49 @@
/*_
* Copyright 2010 Scyphus Solutions Co. Ltd. All rights reserved.
*
* Authors:
* Hirochika Asai
*/
#ifndef _URL_PARSER_H
#define _URL_PARSER_H
/*
* URL storage
*/
struct parsed_url {
char *scheme; /* mandatory */
char *host; /* mandatory */
char *port; /* optional */
char *path; /* optional */
char *query; /* optional */
char *fragment; /* optional */
char *username; /* optional */
char *password; /* optional */
};
#ifdef __cplusplus
extern "C" {
#endif
/*
* Declaration of function prototypes
*/
struct parsed_url *parse_url (const char *);
void parsed_url_free (struct parsed_url *);
#ifdef __cplusplus
}
#endif
#endif /* _URL_PARSER_H */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: sw=4 ts=4 fdm=marker
* vim<600: sw=4 ts=4
*/

View File

@ -0,0 +1,27 @@
The "inih" library is distributed under the New BSD license:
Copyright (c) 2009, Brush Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Brush Technology nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY BRUSH TECHNOLOGY ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL BRUSH TECHNOLOGY BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,9 @@
CFLAGS = -Wall
all: test_ini
test_ini: ini.o test_ini.o
gcc -o test_ini $(CFLAGS) test_ini.o ini.o
clean:
rm -f test_ini *.o

View File

@ -0,0 +1,5 @@
inih is a simple .INI file parser written in C, released under the New BSD
license (see LICENSE.txt). Go to the project home page for more info:
http://code.google.com/p/inih/

View File

@ -0,0 +1,76 @@
// Read an INI file into easy-to-access name/value pairs.
#include <algorithm>
#include <cctype>
#include <cstdlib>
#include "../ini.h"
#include "INIReader.h"
using std::string;
INIReader::INIReader(string filename)
{
_error = ini_parse(filename.c_str(), ValueHandler, this);
}
int INIReader::ParseError()
{
return _error;
}
string INIReader::Get(string section, string name, string default_value)
{
string key = MakeKey(section, name);
return _values.count(key) ? _values[key] : default_value;
}
long INIReader::GetInteger(string section, string name, long default_value)
{
string valstr = Get(section, name, "");
const char* value = valstr.c_str();
char* end;
// This parses "1234" (decimal) and also "0x4D2" (hex)
long n = strtol(value, &end, 0);
return end > value ? n : default_value;
}
double INIReader::GetReal(string section, string name, double default_value)
{
string valstr = Get(section, name, "");
const char* value = valstr.c_str();
char* end;
double n = strtod(value, &end);
return end > value ? n : default_value;
}
bool INIReader::GetBoolean(string section, string name, bool default_value)
{
string valstr = Get(section, name, "");
// Convert to lower case to make string comparisons case-insensitive
std::transform(valstr.begin(), valstr.end(), valstr.begin(), ::tolower);
if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1")
return true;
else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0")
return false;
else
return default_value;
}
string INIReader::MakeKey(string section, string name)
{
string key = section + "." + name;
// Convert to lower case to make section/name lookups case-insensitive
std::transform(key.begin(), key.end(), key.begin(), ::tolower);
return key;
}
int INIReader::ValueHandler(void* user, const char* section, const char* name,
const char* value)
{
INIReader* reader = (INIReader*)user;
string key = MakeKey(section, name);
if (reader->_values[key].size() > 0)
reader->_values[key] += "\n";
reader->_values[key] += value;
return 1;
}

View File

@ -0,0 +1,53 @@
// Read an INI file into easy-to-access name/value pairs.
// inih and INIReader are released under the New BSD license (see LICENSE.txt).
// Go to the project home page for more info:
//
// http://code.google.com/p/inih/
#ifndef __INIREADER_H__
#define __INIREADER_H__
#include <map>
#include <string>
// Read an INI file into easy-to-access name/value pairs. (Note that I've gone
// for simplicity here rather than speed, but it should be pretty decent.)
class INIReader
{
public:
// Construct INIReader and parse given filename. See ini.h for more info
// about the parsing.
INIReader(std::string filename);
// Return the result of ini_parse(), i.e., 0 on success, line number of
// first error on parse error, or -1 on file open error.
int ParseError();
// Get a string value from INI file, returning default_value if not found.
std::string Get(std::string section, std::string name,
std::string default_value);
// Get an integer (long) value from INI file, returning default_value if
// not found or not a valid integer (decimal "1234", "-1234", or hex "0x4d2").
long GetInteger(std::string section, std::string name, long default_value);
// Get a real (floating point double) value from INI file, returning
// default_value if not found or not a valid floating point value
// according to strtod().
double GetReal(std::string section, std::string name, double default_value);
// Get a boolean value from INI file, returning default_value if not found or if
// not a valid true/false value. Valid true values are "true", "yes", "on", "1",
// and valid false values are "false", "no", "off", "0" (not case sensitive).
bool GetBoolean(std::string section, std::string name, bool default_value);
private:
int _error;
std::map<std::string, std::string> _values;
static std::string MakeKey(std::string section, std::string name);
static int ValueHandler(void* user, const char* section, const char* name,
const char* value);
};
#endif // __INIREADER_H__

View File

@ -0,0 +1,21 @@
// Example that shows simple usage of the INIReader class
#include <iostream>
#include "INIReader.h"
int main()
{
INIReader reader("../examples/test.ini");
if (reader.ParseError() < 0) {
std::cout << "Can't load 'test.ini'\n";
return 1;
}
std::cout << "Config loaded from 'test.ini': version="
<< reader.GetInteger("protocol", "version", -1) << ", name="
<< reader.Get("user", "name", "UNKNOWN") << ", email="
<< reader.Get("user", "email", "UNKNOWN") << ", pi="
<< reader.GetReal("user", "pi", -1) << ", active="
<< reader.GetBoolean("user", "active", true) << "\n";
return 0;
}

View File

@ -0,0 +1,8 @@
// CFG(section, name, default)
CFG(protocol, version, "0")
CFG(user, name, "Fatty Lumpkin")
CFG(user, email, "fatty@lumpkin.com")
#undef CFG

View File

@ -0,0 +1,40 @@
/* ini.h example that simply dumps an INI file without comments */
#include <stdio.h>
#include <string.h>
#include "../ini.h"
static int dumper(void* user, const char* section, const char* name,
const char* value)
{
static char prev_section[50] = "";
if (strcmp(section, prev_section)) {
printf("%s[%s]\n", (prev_section[0] ? "\n" : ""), section);
strncpy(prev_section, section, sizeof(prev_section));
prev_section[sizeof(prev_section) - 1] = '\0';
}
printf("%s = %s\n", name, value);
return 1;
}
int main(int argc, char* argv[])
{
int error;
if (argc <= 1) {
printf("Usage: ini_dump filename.ini\n");
return 1;
}
error = ini_parse(argv[1], dumper, NULL);
if (error < 0) {
printf("Can't read '%s'!\n", argv[1]);
return 2;
}
else if (error) {
printf("Bad config file (first error on line %d)!\n", error);
return 3;
}
return 0;
}

View File

@ -0,0 +1,44 @@
/* Example: parse a simple configuration file */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../ini.h"
typedef struct
{
int version;
const char* name;
const char* email;
} configuration;
static int handler(void* user, const char* section, const char* name,
const char* value)
{
configuration* pconfig = (configuration*)user;
#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
if (MATCH("protocol", "version")) {
pconfig->version = atoi(value);
} else if (MATCH("user", "name")) {
pconfig->name = strdup(value);
} else if (MATCH("user", "email")) {
pconfig->email = strdup(value);
} else {
return 0; /* unknown section/name, error */
}
return 1;
}
int main(int argc, char* argv[])
{
configuration config;
if (ini_parse("test.ini", handler, &config) < 0) {
printf("Can't load 'test.ini'\n");
return 1;
}
printf("Config loaded from 'test.ini': version=%d, name=%s, email=%s\n",
config.version, config.name, config.email);
return 0;
}

View File

@ -0,0 +1,46 @@
/* Parse a configuration file into a struct using X-Macros */
#include <stdio.h>
#include <string.h>
#include "../ini.h"
/* define the config struct type */
typedef struct {
#define CFG(s, n, default) char *s##_##n;
#include "config.def"
} config;
/* create one and fill in its default values */
config Config = {
#define CFG(s, n, default) default,
#include "config.def"
};
/* process a line of the INI file, storing valid values into config struct */
int handler(void *user, const char *section, const char *name,
const char *value)
{
config *cfg = (config *)user;
if (0) ;
#define CFG(s, n, default) else if (strcmp(section, #s)==0 && \
strcmp(name, #n)==0) cfg->s##_##n = strdup(value);
#include "config.def"
return 1;
}
/* print all the variables in the config, one per line */
void dump_config(config *cfg)
{
#define CFG(s, n, default) printf("%s_%s = %s\n", #s, #n, cfg->s##_##n);
#include "config.def"
}
int main(int argc, char* argv[])
{
if (ini_parse("test.ini", handler, &Config) < 0)
printf("Can't load 'test.ini', using defaults\n");
dump_config(&Config);
return 0;
}

View File

@ -0,0 +1,10 @@
; Test config file for ini_example.c and INIReaderTest.cpp
[protocol] ; Protocol configuration
version=6 ; IPv6
[user]
name = Bob Smith ; Spaces around '=' are stripped
email = bob@smith.com ; And comments (like this) ignored
active = true ; Test a boolean
pi = 3.14159 ; Test a floating point number

View File

@ -0,0 +1,19 @@
# Simple makefile to build inih as a static library using g++
SRC = ../ini.c
OBJ = $(SRC:.c=.o)
OUT = libinih.a
INCLUDES = -I..
CCFLAGS = -g -O2
CC = g++
default: $(OUT)
.c.o:
$(CC) $(INCLUDES) $(CCFLAGS) $(EXTRACCFLAGS) -c $< -o $@
$(OUT): $(OBJ)
ar rcs $(OUT) $(OBJ) $(EXTRAARFLAGS)
clean:
rm -f $(OBJ) $(OUT)

View File

@ -0,0 +1,176 @@
/* inih -- simple .INI file parser
inih is released under the New BSD license (see LICENSE.txt). Go to the project
home page for more info:
http://code.google.com/p/inih/
*/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "ini.h"
#if !INI_USE_STACK
#include <stdlib.h>
#endif
#define MAX_SECTION 50
#define MAX_NAME 50
/* Strip whitespace chars off end of given string, in place. Return s. */
static char* rstrip(char* s)
{
char* p = s + strlen(s);
while (p > s && isspace((unsigned char)(*--p)))
*p = '\0';
return s;
}
/* Return pointer to first non-whitespace char in given string. */
static char* lskip(const char* s)
{
while (*s && isspace((unsigned char)(*s)))
s++;
return (char*)s;
}
/* Return pointer to first char c or ';' comment in given string, or pointer to
null at end of string if neither found. ';' must be prefixed by a whitespace
character to register as a comment. */
static char* find_char_or_comment(const char* s, char c)
{
int was_whitespace = 0;
while (*s && *s != c && !(was_whitespace && *s == ';')) {
was_whitespace = isspace((unsigned char)(*s));
s++;
}
return (char*)s;
}
/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
static char* strncpy0(char* dest, const char* src, size_t size)
{
strncpy(dest, src, size);
dest[size - 1] = '\0';
return dest;
}
/* See documentation in header file. */
int ini_parse_file(FILE* file,
int (*handler)(void*, const char*, const char*,
const char*),
void* user)
{
/* Uses a fair bit of stack (use heap instead if you need to) */
#if INI_USE_STACK
char line[INI_MAX_LINE];
#else
char* line;
#endif
char section[MAX_SECTION] = "";
char prev_name[MAX_NAME] = "";
char* start;
char* end;
char* name;
char* value;
int lineno = 0;
int error = 0;
#if !INI_USE_STACK
line = (char*)malloc(INI_MAX_LINE);
if (!line) {
return -2;
}
#endif
/* Scan through file line by line */
while (fgets(line, INI_MAX_LINE, file) != NULL) {
lineno++;
start = line;
#if INI_ALLOW_BOM
if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
(unsigned char)start[1] == 0xBB &&
(unsigned char)start[2] == 0xBF) {
start += 3;
}
#endif
start = lskip(rstrip(start));
if (*start == ';' || *start == '#') {
/* Per Python ConfigParser, allow '#' comments at start of line */
}
#if INI_ALLOW_MULTILINE
else if (*prev_name && *start && start > line) {
/* Non-black line with leading whitespace, treat as continuation
of previous name's value (as per Python ConfigParser). */
if (!handler(user, section, prev_name, start) && !error)
error = lineno;
}
#endif
else if (*start == '[') {
/* A "[section]" line */
end = find_char_or_comment(start + 1, ']');
if (*end == ']') {
*end = '\0';
strncpy0(section, start + 1, sizeof(section));
*prev_name = '\0';
}
else if (!error) {
/* No ']' found on section line */
error = lineno;
}
}
else if (*start && *start != ';') {
/* Not a comment, must be a name[=:]value pair */
end = find_char_or_comment(start, '=');
if (*end != '=') {
end = find_char_or_comment(start, ':');
}
if (*end == '=' || *end == ':') {
*end = '\0';
name = rstrip(start);
value = lskip(end + 1);
end = find_char_or_comment(value, '\0');
if (*end == ';')
*end = '\0';
rstrip(value);
/* Valid name[=:]value pair found, call handler */
strncpy0(prev_name, name, sizeof(prev_name));
if (!handler(user, section, name, value) && !error)
error = lineno;
}
else if (!error) {
/* No '=' or ':' found on name[=:]value line */
error = lineno;
}
}
}
#if !INI_USE_STACK
free(line);
#endif
return error;
}
/* See documentation in header file. */
int ini_parse(const char* filename,
int (*handler)(void*, const char*, const char*, const char*),
void* user)
{
FILE* file;
int error;
file = fopen(filename, "r");
if (!file)
return -1;
error = ini_parse_file(file, handler, user);
fclose(file);
return error;
}

View File

@ -0,0 +1,72 @@
/* inih -- simple .INI file parser
inih is released under the New BSD license (see LICENSE.txt). Go to the project
home page for more info:
http://code.google.com/p/inih/
*/
#ifndef __INI_H__
#define __INI_H__
/* Make this header file easier to include in C++ code */
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
/* Parse given INI-style file. May have [section]s, name=value pairs
(whitespace stripped), and comments starting with ';' (semicolon). Section
is "" if name=value pair parsed before any section heading. name:value
pairs are also supported as a concession to Python's ConfigParser.
For each name=value pair parsed, call handler function with given user
pointer as well as section, name, and value (data only valid for duration
of handler call). Handler should return nonzero on success, zero on error.
Returns 0 on success, line number of first error on parse error (doesn't
stop on first error), -1 on file open error, or -2 on memory allocation
error (only when INI_USE_STACK is zero).
*/
int ini_parse(const char* filename,
int (*handler)(void* user, const char* section,
const char* name, const char* value),
void* user);
/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
close the file when it's finished -- the caller must do that. */
int ini_parse_file(FILE* file,
int (*handler)(void* user, const char* section,
const char* name, const char* value),
void* user);
/* Nonzero to allow multi-line value parsing, in the style of Python's
ConfigParser. If allowed, ini_parse() will call the handler with the same
name for each subsequent line parsed. */
#ifndef INI_ALLOW_MULTILINE
#define INI_ALLOW_MULTILINE 1
#endif
/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
the file. See http://code.google.com/p/inih/issues/detail?id=21 */
#ifndef INI_ALLOW_BOM
#define INI_ALLOW_BOM 1
#endif
/* Nonzero to use stack, zero to use heap (malloc/free). */
#ifndef INI_USE_STACK
#define INI_USE_STACK 1
#endif
/* Maximum line length for any line in INI file. */
#ifndef INI_MAX_LINE
#define INI_MAX_LINE 200
#endif
#ifdef __cplusplus
}
#endif
#endif /* __INI_H__ */

View File

@ -0,0 +1,6 @@
[protocol]
version = 5 ; próba komment
[user]
name = Gipsz Jakab ; comment
email = gip@szja.kab ; comment

View File

@ -0,0 +1,45 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include "ini.h"
typedef struct {
int version;
const char* name;
const char* email;
} configuration;
static int handler(void* user, const char* section, const char* name,
const char* value)
{
configuration *pconfig = (configuration*)user;
#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
if (MATCH("protocol", "version")) {
pconfig->version = atoi(value);
} else if (MATCH("user", "name")) {
pconfig->name = strdup(value);
} else if (MATCH("user", "email")) {
pconfig->email = strdup(value);
} else {
return 0; /* unknown section/name, error */
}
return 1;
}
int main (int argc, char* argv[]) {
configuration config;
if (ini_parse("test.ini", handler, &config) < 0) {
printf("Can't load 'test.ini'\n");
return 1;
}
printf("Config loaded from 'test.ini': version=%d, name=%s, email=%s\n",
config.version, config.name, config.email);
return 0;
}

View File

@ -0,0 +1 @@
This is an error

View File

@ -0,0 +1 @@
indented

View File

@ -0,0 +1,5 @@
[section1]
name1=value1
[section2
[section3 ; comment ]
name2=value2

View File

@ -0,0 +1,47 @@
no_file.ini: e=-1 user=0
... [section1]
... one=This is a test;
... two=1234;
... [ section 2 ]
... happy=4;
... sad=;
... [comment_test]
... test1=1;2;3;
... test2=2;3;4;this won't be a comment, needs whitespace before ';';
... test;3=345;
... test4=4#5#6;
... [colon_tests]
... Content-Type=text/html;
... foo=bar;
... adams=42;
normal.ini: e=0 user=101
... [section1]
... name1=value1;
... name2=value2;
bad_section.ini: e=3 user=102
bad_comment.ini: e=1 user=102
... [section]
... a=b;
... user=parse_error;
... c=d;
user_error.ini: e=3 user=104
... [section1]
... single1=abc;
... multi=this is a;
... multi=multi-line value;
... single2=xyz;
... [section2]
... multi=a;
... multi=b;
... multi=c;
... [section3]
... single=ghi;
... multi=the quick;
... multi=brown fox;
... name=bob smith;
multi_line.ini: e=0 user=105
bad_multi.ini: e=1 user=105
... [bom_section]
... bom_name=bom_value;
... key“=value“;
bom.ini: e=0 user=107

View File

@ -0,0 +1,43 @@
no_file.ini: e=-1 user=0
... [section1]
... one=This is a test;
... two=1234;
... [ section 2 ]
... happy=4;
... sad=;
... [comment_test]
... test1=1;2;3;
... test2=2;3;4;this won't be a comment, needs whitespace before ';';
... test;3=345;
... test4=4#5#6;
... [colon_tests]
... Content-Type=text/html;
... foo=bar;
... adams=42;
normal.ini: e=0 user=101
... [section1]
... name1=value1;
... name2=value2;
bad_section.ini: e=3 user=102
bad_comment.ini: e=1 user=102
... [section]
... a=b;
... user=parse_error;
... c=d;
user_error.ini: e=3 user=104
... [section1]
... single1=abc;
... multi=this is a;
... single2=xyz;
... [section2]
... multi=a;
... [section3]
... single=ghi;
... multi=the quick;
... name=bob smith;
multi_line.ini: e=4 user=105
bad_multi.ini: e=1 user=105
... [bom_section]
... bom_name=bom_value;
... key“=value“;
bom.ini: e=0 user=107

View File

@ -0,0 +1,3 @@
[bom_section]
bom_name=bom_value
key“ = value“

View File

@ -0,0 +1,15 @@
[section1]
single1 = abc
multi = this is a
multi-line value
single2 = xyz
[section2]
multi = a
b
c
[section3]
single: ghi
multi: the quick
brown fox
name = bob smith ; comment line 1
; comment line 2

View File

@ -0,0 +1,25 @@
; This is an INI file
[section1] ; section comment
one=This is a test ; name=value comment
two = 1234
; x=y
[ section 2 ]
happy = 4
sad =
[empty]
; do nothing
[comment_test]
test1 = 1;2;3 ; only this will be a comment
test2 = 2;3;4;this won't be a comment, needs whitespace before ';'
test;3 = 345 ; key should be "test;3"
test4 = 4#5#6 ; '#' only starts a comment at start of line
#test5 = 567 ; entire line commented
# test6 = 678 ; entire line commented, except in MULTILINE mode
[colon_tests]
Content-Type: text/html
foo:bar
adams : 42

View File

@ -0,0 +1,2 @@
@call tcc ..\ini.c -I..\ -run unittest.c > baseline_multi.txt
@call tcc ..\ini.c -I..\ -DINI_ALLOW_MULTILINE=0 -run unittest.c > baseline_single.txt

View File

@ -0,0 +1,58 @@
/* inih -- unit tests
This works simply by dumping a bunch of info to standard output, which is
redirected to an output file (baseline_*.txt) and checked into the Subversion
repository. This baseline file is the test output, so the idea is to check it
once, and if it changes -- look at the diff and see which tests failed.
Here's how I produced the two baseline files (with Tiny C Compiler):
tcc -DINI_ALLOW_MULTILINE=1 ../ini.c -run unittest.c > baseline_multi.txt
tcc -DINI_ALLOW_MULTILINE=0 ../ini.c -run unittest.c > baseline_single.txt
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../ini.h"
int User;
char Prev_section[50];
int dumper(void* user, const char* section, const char* name,
const char* value)
{
User = (int)user;
if (strcmp(section, Prev_section)) {
printf("... [%s]\n", section);
strncpy(Prev_section, section, sizeof(Prev_section));
Prev_section[sizeof(Prev_section) - 1] = '\0';
}
printf("... %s=%s;\n", name, value);
return strcmp(name, "user")==0 && strcmp(value, "parse_error")==0 ? 0 : 1;
}
void parse(const char* fname) {
static int u = 100;
int e;
*Prev_section = '\0';
e = ini_parse(fname, dumper, (void*)u);
printf("%s: e=%d user=%d\n", fname, e, User);
u++;
}
int main(void)
{
parse("no_file.ini");
parse("normal.ini");
parse("bad_section.ini");
parse("bad_comment.ini");
parse("user_error.ini");
parse("multi_line.ini");
parse("bad_multi.ini");
parse("bom.ini");
return 0;
}

View File

@ -0,0 +1,4 @@
[section]
a = b
user = parse_error
c = d

View File

@ -0,0 +1,165 @@
/* logger.c
* Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <stdarg.h>
#include "logger.h"
#include "debug.h"
#include "misc.h"
#include "utlist.h"
#include "misc.h"
//~ #define concat(dst, src) strncat((dst), (src), ((sizeof(dst)) - strlen(dst) - 1))
//~ #define concatf(dst, ...) snprintf((dst) + strlen((dst)), sizeof((dst)) - strlen((dst)), __VA_ARGS__)
static char *logfile = NULL;
static char logfile_buf[128];
/* TODO: rendbe tenni ezt a marhaságot valami kultúrált megoldással úgy,
hogy a hívó függvény által átadott stringet lemásoljuk és helyben a
másolatot kezeljük. Azért, hogy a hívó utána a saját mutatójával azt
csinálhasson, amit csak akar. Kezelni a NULL-t, a default értékeket és
valami kultúrált köntöst adni neki, ne ezt a három változós izét. */
static char *timestamp_format_default = "%a %H:%M:%S";
static char *timestamp_format = "%a %H:%M:%S";
static char timestamp_format_buf[128];
static int initialised = 0;
struct timeval first_time = {0, 0};
char ela[16];
typedef struct el {
struct timeval tm;
char *line;
struct el *prev; /* needed for a doubly-linked list only */
struct el *next; /* needed for singly- or doubly-linked lists */
} el;
/* Ha a tv (első paraméter) meg van adva, akkor nincs gettimeofday hívás, hanem
* a tv-ben beállított idő lesz a mérvadó. Ha a tv értéke NULL, akkor a
* jelenlegi idővel (gettimeofday) számolunk. Ha meg van adva a ptm, akkor az is
* be lesz állítva. Ha az elapsed nem NULL, akkor kiszámolja az indítástól
* eltelt időt, és visszaad egy mutatót, ami a kiszámolt érték ssss.mmmu
* formátumú stringre mutat.
*/
static struct timeval get_time (struct timeval *tv, struct tm **ptm, char *elapsed) {
struct timeval mytv;
if (tv == NULL) {
gettimeofday(&mytv, NULL);
} else {
mytv.tv_sec = tv->tv_sec;
mytv.tv_usec = tv->tv_usec;
}
if (tv)
*tv = mytv;
if (ptm)
*ptm = localtime(&mytv.tv_sec); // thread safe?
if (elapsed) {
long int delta = ((mytv.tv_sec * 1000000) + mytv.tv_usec) - ((first_time.tv_sec * 1000000) + first_time.tv_usec);
snprintf(elapsed, 8, "%3.3f", (float)delta / 1000000);
}
return mytv;
}
void con_init () {
if (initialised)
return;
first_time = get_time(NULL, NULL, NULL);
initialised = 1;
return;
}
void con_logfile (const char *file) {
if (file == NULL) {
logfile = NULL;
return;
}
strncpy(logfile_buf, file, sizeof(logfile_buf));
logfile = logfile_buf;
}
void con_timestamp_format (const char *format) {
if (format == NULL) {
timestamp_format = timestamp_format_default;
return;
}
strncpy(timestamp_format_buf, format, sizeof(timestamp_format_buf));
timestamp_format = timestamp_format_buf;
}
// gány, memóriazabáló függvény :)
void _con_writef (enum con_callmode cm, char *file, int line, const char *function, const char *fmt, ...) {
if (!initialised)
con_init();
// botrányos deklarációk
char tmp[1024] = {0}; // ide kerül a végeredmény
char tmp2[1024]; // va_arg szöveges kimenete
char timestamp[64]; // timestamp
char elapsed[64]; // elapsed
va_list ap;
va_start(ap, fmt);
vsnprintf(tmp2, sizeof(tmp2), fmt, ap);
va_end(ap);
// TODO: ezt az idő lekérdezős részt külön függvénybe kell rakni!
struct timeval tv;
struct tm *ptm;
tv = get_time(NULL, &ptm, NULL); // idő, ami alapján a továbbiakban számolunk
strftime(timestamp, sizeof(timestamp), timestamp_format, ptm); // szöveges timestamp
get_time(&tv, &ptm, elapsed); // elapsed lekérdezése TODO: ez borzalmas, átírni!!!
switch (cm) {
case CON_CALLMODE_CONFT:
case CON_CALLMODE_CONFTN:
concatf(tmp, "%s %s", timestamp, tmp2);
break;
case CON_CALLMODE_CONF:
case CON_CALLMODE_CONFN:
strncpy(tmp, tmp2, sizeof(tmp) - 1);
break;
case CON_CALLMODE_DEBUG:
concatf(tmp, "%s /%s/ [%s:%d %s]: %s", timestamp, elapsed, file, line, function, tmp2);
break;
}
chomp(tmp);
// Kiiratás
printf("%s", tmp);
if (cm != CON_CALLMODE_CONFTN && cm != CON_CALLMODE_CONFN)
printf("\n");
fflush(stdout);
if (logfile) {
FILE *f = fopen(logfile, "a");
if (f != NULL) {
fprintf(f, "%s", tmp);
if (cm != CON_CALLMODE_CONFTN && cm != CON_CALLMODE_CONFN)
fprintf(f, "\n");
fclose(f);
}
}
}

View File

@ -0,0 +1,45 @@
/* logger.h
* Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*
conf(...) kiírás időbélyeg nélkül, újsorral
confn(...) kiírás időbélyeg nélkül, újsor nélkül
conft(...) kiírás időbélyeggel, újsorral
conftn(...) kiírás időbélyeggel, újsor nélkül
*/
enum con_callmode {
CON_CALLMODE_CONF,
CON_CALLMODE_CONFN,
CON_CALLMODE_CONFT,
CON_CALLMODE_CONFTN,
CON_CALLMODE_DEBUG,
};
#ifndef LOGGER_H_LOADED
#define LOGGER_H_LOADED
void con_init ();
//~ void conft (const char *fmt, ...);
void con_logfile (const char *file);
void con_timestamp_format (const char *format);
void _con_writef (enum con_callmode cm, char *file, int line, const char *function, const char *fmt, ...);
#endif
// ha a DEBUG makró 1, akkor a debug() makrók életbe lépnek,
// ellenkező esetben a kódba sem kerül bele :)
#ifdef CON_DEBUG
#define con_debug(...) _con_writef(CON_CALLMODE_DEBUG, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
#else
#define con_debug(...)
#endif
#define conf(...) _con_writef(CON_CALLMODE_CONF, NULL, 0, NULL, __VA_ARGS__)
#define confn(...) _con_writef(CON_CALLMODE_CONFN, NULL, 0, NULL, __VA_ARGS__)
#define conft(...) _con_writef(CON_CALLMODE_CONFT, NULL, 0, NULL, __VA_ARGS__)
#define conftn(...) _con_writef(CON_CALLMODE_CONFTN, NULL, 0, NULL, __VA_ARGS__)

View File

@ -0,0 +1,43 @@
/* logger_test.c
* Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
#define CON_DEBUG
#include "logger.h"
#include <stdio.h>
#include <errno.h>
int main () {
con_logfile("logger_test.log");
con_timestamp_format("%a %H:%M:%S");
conft("zsiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiir");
conft("yeahhhh");
conft("ok");
FILE *f;
if (!(f = fopen("/proc/ioports", "r"))) {
perror("fopen");
return 1;
}
char tmp[128];
int i = 0;
while (fgets(tmp, sizeof(tmp), f)) {
con_debug("i=%d %s", i, tmp);
i++;
}
fclose(f);
return 0;
}

View File

@ -0,0 +1,285 @@
/* misc.c
* Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/* altalanos fuggvenyek */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <arpa/inet.h> // inet_pton() függvény
#include <stdarg.h>
#include "debug.h"
char *chomp (char *s) {
if (s == NULL)
return NULL;
size_t len = strlen(s);
int n;
for (n = len - 1; n >= 0; n--) {
if (s[n] == '\n' || s[n] == '\r')
s[n] = '\0';
else
return s;
}
return s;
}
/*
fmtsub - mintából, változó-érték párokból és készít stringet behelyettesítéssel
paraméterek:
dest ebbe a stringbe menti el az eredményt
size dest mérete
pattern minta
args változó-érték táblázat, nullával lezárva
visszatérési érték:
0 sikerült a behelyettesítés
-1 valamelyik mutató == NULL vagy a size == 0
A táblázat megadása:
const char *args[][2] = {
{"%n", "2463434"},
{"%v", "0.9.6-rc2"},
{"version", "Linux 2.6.32"},
{"boo", "baa"},
{0}}; */
int fmtsub (char *dest, size_t size, const char *pattern, const char *args[][2]) {
char buf[4096];
//~ debs(dest); debi(size); debs(pattern); debi(args);
if (dest == NULL || pattern == NULL || args == NULL || size == 0)
return -1;
// ha a pattern üres string, akkor üres stringet adunk vissza
if (strlen(pattern) == 0) {
strcpy(dest, "");
return 0;
}
int pospat, posbuf, a;
int lenpat = strlen(pattern);
buf[0] = '\0';
// byteonként végigmegyünk a pattern stringen
for (pospat = 0, posbuf = 0; pospat < lenpat && posbuf < sizeof(buf) - 1;) {
const char *subpat = &pattern[pospat];
int match = 0;
// végigmegyünk az args táblázaton
for (a = 0; args[a][0] != 0; a++) {
const char *var = args[a][0]; // "%n"
const char *exp = args[a][1]; // "2463434"
// ha a változónév megtalálható a pattern jelenlegi pozíciójánál
if (strlen(var) != 0 && !strncmp(subpat, var, strlen(var))) {
if (exp != NULL) {
// bemásoljuk a változó értékét a bufferbe
strncat(buf, exp, sizeof(buf) - posbuf - 1);
posbuf += strlen(exp);
}
/* ezen a ponton a posbuf nagyobb lehet, mint a buf, ami
igen kellemetlenül érintené a lenti buf[posbuf] = '\0'
értékadást, ezért a posbuf pozícionálót beállítjuk a buf
utolsó byte-jára. ide fog kerülni a NULL. */
if (posbuf >= sizeof(buf) - 1)
posbuf = sizeof(buf) - 1;
// pattern pozícióját a változónév utáni karakterre állítjuk
pospat += strlen(var);
// kihagyjuk a lenti if-et
match = 1;
break;
}
}
// 1 bájt másolása pattern-ből buf-ba
if (!match) {
buf[posbuf] = pattern[pospat];
buf[posbuf+1] = '\0';
pospat++;
posbuf++;
}
}
buf[posbuf] = '\0'; // lezáró NULL
strncpy(dest, buf, size - 1);
return 0;
}
// http://en.wikipedia.org/wiki/ROT13
// paraméter az a string, amit módosítani kell
// visszatérés ugyan az, mint a paraméter kompatibilitási izé miatt
char *encode_rot13 (char *s) {
if (s == NULL)
return NULL;
int i;
for (i = 0; s[i]; i++) {
if (s[i] >= 'a' && s[i] <= 'm') { s[i] += 13; continue; }
if (s[i] >= 'A' && s[i] <= 'M') { s[i] += 13; continue; }
if (s[i] >= 'n' && s[i] <= 'z') { s[i] -= 13; continue; }
if (s[i] >= 'N' && s[i] <= 'Z') { s[i] -= 13; continue; }
}
return s;
}
// szóközöket és tabokat vág le a megadott string bal és/vagy jobb oldaláról
// trim - mindkét oldalról vág
// ltrim - bal oldalról vág
// rtrim - jobb oldalról vág
char *_trim (char *s, int trim_from_left, int trim_from_right) {
if (s == NULL)
return NULL;
int n, i;
size_t len = strlen(s);
if (trim_from_right) {
for (n = len - 1; n >= 0; n--) {
if (s[n] == 32 || s[n] == '\t') {
s[n] = '\0';
len--;
} else {
break;
}
}
}
if (trim_from_left) {
for (n = 0; n < len; n++)
if (s[n] != 32 && s[n] != '\t')
break;
if (n > 0) {
for (i = n; i < len; i++)
s[i-n] = s[i];
for (i = len - n; i < len; i++)
s[i] = '\0';
}
}
return s;
}
/* megszámlálja a <delimeter> elemeket */
static int split_get_size (char *buffer, int delimeter) {
int c = 0, i = 0;
if (buffer[ 0 ] == 0)
return 0;
while (buffer[i] != 0) {
if (buffer[i] == delimeter)
c++;
i++;
}
/* egyet hozzáadok, mert a "hello" is értéknek számít, hiába nincs benne ';' */
return c + 1;
}
/* visszatér egy pointer tömbbel, ami az elemekre mutat, a tömböt 0 pointerrel zárja */
char **split (char *buffer, int delimeter) {
int size = split_get_size(buffer, delimeter);
/* helyfoglalás a pointer tömbnek */
char **res = (char **)malloc(sizeof(char *) * (size + 1));
int i=0;
int p=0;
while (buffer[i] != 0) {
/* a sor elejét eltárolom */
res[p++] = buffer + i;
/* a sor végét megkeresem */
do {
i++;
} while (buffer[i] != 0 && buffer[i] != delimeter);
/* felülírjuk a ';'-t 0-val */
if (buffer[i] != 0) {
buffer[i] = 0;
i++;
}
}
/* null pointerrel zárjuk */
res[p] = 0;
return res;
}
// 1-et ad vissza, ha érvényes a kapott IP cím
// 0-át ha nem
int is_valid_ip (const char *ip) {
struct sockaddr_in sa;
int result = inet_pton(AF_INET, ip, &(sa.sin_addr));
return result != 0;
}
void die (const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
exit(-1);
}
void read_lines_from_stdin (char *dst, int size) {
char *line = NULL; // ideiglenes buffer a getline-nak
size_t len = 0;
ssize_t read;
int remaining_size; // dst stringbe még ennyi byte fér el
remaining_size = size - 1;
while ((read = getline(&line, &len, stdin)) != -1) {
strncat(dst, line, remaining_size);
remaining_size -= read;
if (remaining_size <= 0)
break;
}
chomp(dst); // utolsó \n karakter chompolása
free(line);
}
char *strcutpbrk (char *str, const char *accept) {
if (str == NULL)
return NULL;
if (accept == NULL)
return str;
char *at = strpbrk(str, accept);
if (at != NULL)
*at = '\0';
return str;
}
char *strdelchars (char *str, const char *dels) {
if (!str)
return NULL;
if (!dels)
return str;
int len_str = strlen(str);
int len_dels = strlen(dels);
if (!len_str || !len_dels)
return str;
int i, j, k = 0;
for (i = 0; i < len_str; i++) {
for (j = 0; j < len_dels; j++)
if (str[i] == dels[j])
break;
if (j == len_dels) // ha nem volt break
str[k++] = str[i];
}
str[k] = '\0';
return str;
}

View File

@ -0,0 +1,58 @@
/* misc.h
* Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
char *chomp (char *str);
#define scpy(dst, src) strncpy((dst), (src), (sizeof(dst)) - 1)
#define concat(dst, src) strncat((dst), (src), ((sizeof(dst)) - strlen(dst) - 1))
#define concatf(dst, ...) snprintf((dst) + strlen((dst)), sizeof((dst)) - strlen((dst)), __VA_ARGS__)
int fmtsub (char *dest, size_t size, const char *pattern, const char *args[][2]);
char *encode_rot13 (char *s);
char *_trim (char *s, int trim_from_left, int trim_from_right);
#define trim(s) _trim(s, 1, 1)
#define ltrim(s) _trim(s, 1, 0)
#define rtrim(s) _trim(s, 0, 1)
// Az str stringet levágja ott, ahol az accept stringben felsorolt első
// karakterrel találkozik. A levágást úgy oldja meg, hogy az str string
// megfelelő byte-ját átírja 0-ra. A visszatérési érték az str string. Ha az
// accept == NULL, akkor nem piszkálja az str stringet. Ha az str == NULL,
// akkor a visszatérési érték NULL. Például ha az str "Bandi" és az accept
// "cde", akkor az eredmény "Ban" lesz.
char *strcutpbrk (char *str, const char *accept);
// Nincs még doksi, ezért ide írom...
// A chomp, trim, encode_rot13, strcutpbrk függvények használhatok beágyazva is, tehát:
// char buf[64];
// encode_rot13(trim(chomp(buf)));
// visszatér egy pointer tömbbel, ami az elemekre mutat, a tömböt 0 pointerrel zárja. A függvény felülírja a buffer stringben lévő delimetereket. Magyarul módosítja a kapott stringet, ami nem biztos, hogy jó dolog. Pl. char *x = "hello" változóknál segfault.
char **split (char *buffer, int delimeter);
// 1-et ad vissza, ha érvényes a kapott IP cím
// 0-át ha nem
int is_valid_ip (const char *ip);
// mint Perl-ben:)
void die (const char *fmt, ...);
// STDIN-ről beolvasás EOF-ig. A sorokat összefűzi és a dst stringbe menti,
// vigyázva a lezáró NULL karakterre és a dst string hosszára, amit a size-ben
// kell átadni. Az utolsó \n karakterek le lesznek csípve.
void read_lines_from_stdin (char *dst, int size);
// Az str stringből eltávolítja a dels stringben megtalálható karaktereket. Az
// eltávolítás folyamán az str stringben a byteokat fokozatosan balra rendezi.
// A művelet végén elhelyezi a stringet lezáró NULL karaktert. Az eredmény
// ugyan akkora vagy kisebb méretű string. A visszatérési érték az str string.
// Ha az str NULL, akkor a visszatérési érték NULL. Ha a dels NULL, akkor a
// visszatérési érték az str string.
char *strdelchars (char *str, const char *dels);

View File

@ -0,0 +1,524 @@
/* netsocket.c
* Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
// TODO: datapipe-nál jól látható, hogy a connect előtt bejött adatok
// nem kerülnek kiküldésre. Valami buffer megoldást kéne alkalmazni. vagy mégsem?
// ... végülis a Centauri nézőpontja szerint connect előtt miért küldenénk adatokat?
// TODO: gethostbyaddr és gethostbyname függvényeket lecserélni getaddrinfo és
// getnameinfo függvényekre
//
// Figyelem! Amelyik függvényben invoke_callback van, annak a legaljára kell
// destroy_netsocket. Na meg ezt a kommentet átfogalmazni, mert valószínűleg
// baromira nem fogom érteni két hét múlva, hogy mi a szar is akar ez lenni
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <sys/socket.h>
#include <resolv.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <stdarg.h>
#include "netsocket.h"
#include "debug.h"
static void dummy_callback (netsocket_t *obj, int event) {
printf("dummy_callback called at %s:%d\n", __FILE__, __LINE__);
printf(" event = %d\n mode = %s\n host = %s\n port = %d\n lhost = %s\n lport = %d\n",
event,
(obj->mode == NETSOCKET_SERVER) ? "server" : "client",
obj->host,
obj->port,
obj->lhost,
obj->lport
);
}
static void sock_close (netsocket_t *obj) {
if (!obj->sock)
return;
ev_io_stop(obj->loop, &obj->w_in);
ev_io_stop(obj->loop, &obj->w_out);
ev_timer_stop(obj->loop, &obj->w_connect_timeout);
close(obj->sock);
obj->sock = 0;
}
static void invoke_callback (netsocket_t *obj, int event) {
obj->in_callback++;
obj->callback(obj, event);
obj->in_callback--;
// késleltetett destroy
if (obj->destroy_request) {
if (!obj->in_callback) {
netsocket_destroy(obj);
}
}
}
static void disconnect (netsocket_t *obj, char *reason, int ignore_callback) {
if (obj == NULL)
return;
sock_close(obj);
if (reason != NULL)
strncpy(obj->disconnect_reason, reason, sizeof(obj->disconnect_reason) - 1);
if (!ignore_callback)
invoke_callback(obj, NETSOCKET_EVENT_DISCONNECT);
//~ if (!obj->connected)
//~ return;
obj->connected = 0;
// automatikusan felszabaditjuk a gyermek objektumot abban az esetben, ha
// az obj kliens modban van es bejovo kapcsolatrol van szo, maskeppen mondva
// akkor, ha egy szerver objektum gyermek objektuma szakad meg
// ilyen objektumot a sock_accept allokal malloccal
//~ if (obj->mode == NETSOCKET_CLIENT && obj->direction == NETSOCKET_IN)
//~ netsocket_destroy(obj);
}
/**
* @brief Disconnect from peer, close socket
* @param obj the netsocket object
* @param reason description of disconnect reason
*
* If connection in progress, abort the connection and close the socket.
* If connection was established, disconnect from peer, close socket and
* detach from event loop. Call the callback with NETSOCKET_EVENT_DISCONNECT
* event. Save the "reason" to obj->reason.
*/
void netsocket_disconnect (netsocket_t *obj, char *reason) {
disconnect(obj, reason, 1); // 1 jelzi, hogy nem kérünk callback hívást
}
void netsocket_disconnect_withevent (netsocket_t *obj, char *reason) {
disconnect(obj, reason, 0); // 0 jelzi, hogy kérünk callback hívást
}
// ha ezt meghívjuk, akkor a bejövő kapcsolatoknál nem kérdezzük meg
// a kliens IP címének nevét a DNS szervertől
void netsocket_disable_lookup_on_accept (netsocket_t *obj) {
obj->disable_lookup_on_accept = 1;
}
static int sock_accept (netsocket_t *parent) {
netsocket_t *obj = netsocket_new(dummy_callback, NULL, parent->loop);
obj->mode = NETSOCKET_CLIENT;
obj->direction = NETSOCKET_IN;
socklen_t addrlen = sizeof(obj->addr);
obj->sock = accept(parent->sock, (struct sockaddr *) &obj->addr, &addrlen);
if (obj->sock < 0) { // TODO rendes hibakezelés, memória felszabadítás
perror("accept"); // például: Too many open files
netsocket_destroy(obj);
return -1;
}
// kliens IP cím lekérdezése, ha nincs beállítva a "disable_lookup_on_accept"
if (!parent->disable_lookup_on_accept)
obj->hostent = gethostbyaddr(&obj->addr.sin_addr, sizeof(&obj->addr.sin_addr), AF_INET);
// ha nincs hostja az IP-nek, akkor a hostent NULL lesz
if (obj->hostent != NULL)
strncpy(obj->host, obj->hostent->h_name, sizeof(obj->host) - 1);
// IP cím tárolása szöveges formátumban
strncpy(obj->ip, inet_ntoa(obj->addr.sin_addr), sizeof(obj->ip) - 1);
// ha nincs hostja az IP-nek, akkor az IP lesz a host
if (!strlen(obj->host))
strncpy(obj->host, obj->ip, sizeof(obj->host) - 1);
// Kliens portja
obj->port = ntohs(obj->addr.sin_port);
ev_io_set(&obj->w_in, obj->sock, EV_READ);
ev_io_set(&obj->w_out, obj->sock, EV_WRITE);
ev_io_start(obj->loop, &obj->w_in);
ev_io_start(obj->loop, &obj->w_out);
obj->parent = parent;
obj->callback = parent->callback;
obj->userdata = parent->userdata;
strncpy(obj->lhost, parent->lhost, sizeof(obj->lhost));
obj->lport = parent->lport;
return 0;
}
static void w_connect_timeout_cb (EV_P_ ev_io *w, int revents) {
netsocket_t *obj = w->data;
disconnect(obj, "Connection timed out", 0);
}
static void w_in_cb (EV_P_ ev_io *w, int revents) {
netsocket_t *obj = w->data;
int i;
// ha a szerver portra csatlakozott új kliens
if (obj->mode == NETSOCKET_SERVER) {
sock_accept(obj);
return;
}
/* Ide akkor kerülünk, amikor egy kliensről adat érkezik.
* A NETSOCKET_EVENT_CONNECT eseményt és az obj->connected 1-re állítását
* a w_out_cb() függvény okozza, de az valójában a sock_accept() függvényben
* lenne esedékes. Szervernél, amikor várjuk a klienseket, normális esetben
* nincs ezzel semmmi gond, de ha valgrind-el fut, akkor valamiért
* összekeveredik az event loop-ban a sorrend és előbb hívódik meg az
* új kliens objektummal a NETSOCKET_EVENT_READ, mint a NETSOCKET_EVENT_CONNECT.
* Ez a hívó kódjában okozhat kollóziót (és okozott is), ezért ha nincs
* beállítva az obj->connected, de mégis adatot akarnánk beolvasni, akkor
* egyszerűen csak "elnapoljuk" a feladatot. Ekkor az event loop megteszi
* prevenciós körét a w_out_cb() függvényben is, ahol megtörténik a
* NETSOCKET_EVENT_CONNECT és utána ismét visszatér ide. Ezzel kényszerítjük
* ki a helyes sorrendet.
*/
if (!obj->connected)
return;
// adat beolvasása a kliensről
i = read(obj->sock, obj->inbuf, sizeof(obj->inbuf));
if (i < 1) {
disconnect(obj, (i == 0) ? "Connection reset by peer" : strerror(errno), 0);
} else {
obj->inbuf_len = i;
invoke_callback(obj, NETSOCKET_EVENT_READ);
}
}
static int sockerr (int sock) {
int optval = 0;
int err;
socklen_t optlen = sizeof(optval);
err = getsockopt(sock, SOL_SOCKET, SO_ERROR, &optval, &optlen);
if (err) return err;
return optval;
}
static void w_out_cb (EV_P_ ev_io *w, int revents) {
netsocket_t *obj = w->data;
//~ printf("Can't connect to %s: %s\n", obj->host, strerror(sockerr(obj->sock)));
obj->err = sockerr(obj->sock);
ev_io_stop(EV_A_ w);
if (obj->err) {
disconnect(obj, strerror(obj->err), 0);
} else {
obj->connected = 1;
ev_timer_stop(obj->loop, &obj->w_connect_timeout);
invoke_callback(obj, NETSOCKET_EVENT_CONNECT);
}
}
/**
* @brief Make outgoing connection to remote host
* @param obj the netsocket object
* @returns 0 if everything ok, otherwise -1
*
* Use netsocket as client. The function check the syntax of the "host" and "port" variable and
* lookup the host. If the host resolved successfully, then create an
* non-blocking outgoing socket, call connect(3) and add the socket to
* the event loop. In case of problem, close socket and call callback
* with event NETSOCKET_EVENT_DISCONNECT.
*/
int netsocket_connect (netsocket_t *obj) {
obj->mode = NETSOCKET_CLIENT;
obj->direction = NETSOCKET_OUT;
if (obj->host == NULL) {
disconnect(obj, "Invalid host", 0);
return -1;
}
if (obj->port < 1 || obj->port > 65535) {
disconnect(obj, "Invalid port", 0);
return -1;
}
// TODO aszinkronná tenni a host lookup-ot
if ((obj->hostent = gethostbyname(obj->host)) == NULL) {
disconnect(obj, "Host not found", 0);
return -1;
}
struct in_addr **pptr;
pptr = (struct in_addr **)obj->hostent->h_addr_list;
strncpy(obj->ip, inet_ntoa(**(pptr++)), sizeof(obj->ip));
obj->sock = socket(PF_INET, SOCK_STREAM, 0);
ev_io_set(&obj->w_in, obj->sock, EV_READ);
ev_io_set(&obj->w_out, obj->sock, EV_WRITE);
ev_timer_set(&obj->w_connect_timeout, (float)obj->connect_timeout / 1000, 0);
ev_io_start(obj->loop, &obj->w_in);
ev_io_start(obj->loop, &obj->w_out);
ev_timer_start(obj->loop, &obj->w_connect_timeout);
// socket non-block
fcntl(obj->sock, F_SETFL, fcntl(obj->sock, F_GETFL, 0) | O_NONBLOCK);
bzero(&obj->addr, sizeof(obj->addr));
obj->addr.sin_family = AF_INET;
obj->addr.sin_port = htons(obj->port);
obj->addr.sin_addr.s_addr = *(long*)(obj->hostent->h_addr);
connect(obj->sock, (struct sockaddr*)&obj->addr, sizeof(obj->addr));
if (errno != EINPROGRESS) {
netsocket_disconnect(obj, strerror(errno));
return -1;
}
return 0;
}
/**
* @brief Listening for incoming connections
* @param obj the netsocket object
* @returns 0 if everything ok, otherwise -1
*
* Use netsocket as server. The function check the syntax of
* the "lhost" and "lport" variable and
* lookup the lhost. If the host resolved successfully, then create an
* non-blocking incoming socket, bind to lhost, and add the socket to
* the event loop. If "lhost" is NULL, then "0.0.0.0" is assumed.
*
* When a peer connects to the listening port, create a new netsocket object and inherit
* lhost, lport, userdata and callback variables from the server object.
* Accept the incoming connection,
* add them to the event loop and call the callback with NETSOCKET_EVENT_CONNECT event.
* The callback's object parameter is the newly created client object. The server object's
* address is in obj->parent pointer. For each incoming connection make individually a
* new netsocket object.
*/
int netsocket_listen (netsocket_t *obj) {
obj->mode = NETSOCKET_SERVER;
if (!strlen(obj->lhost))
strcpy(obj->lhost, "0.0.0.0");
if (obj->lport < 1 || obj->lport > 65535) {
disconnect(obj, "Invalid local port", 0);
return -1;
}
// TODO aszinkronná tenni a host lookup-ot
if ((obj->hostent = gethostbyname(obj->lhost)) == NULL) {
disconnect(obj, "Local host not found", 0);
return -1;
}
struct in_addr **pptr;
pptr = (struct in_addr **)obj->hostent->h_addr_list;
strncpy(obj->ip, inet_ntoa(**(pptr++)), sizeof(obj->ip));
// TODO: socket függvény hibájának csekkolása
obj->sock = socket(AF_INET, SOCK_STREAM, 0);
fcntl(obj->sock, F_SETFL, fcntl(obj->sock, F_GETFL, 0) | O_NONBLOCK);
int optval = 1;
if (setsockopt(obj->sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) {
perror("setsockopt"); // TODO normális hibakezelés
return -1;
}
bzero(&obj->addr, sizeof(obj->addr));
obj->addr.sin_family = AF_INET;
obj->addr.sin_port = htons(obj->lport);
obj->addr.sin_addr.s_addr = *(long*)(obj->hostent->h_addr);
if (bind(obj->sock, (struct sockaddr *) &obj->addr, sizeof(obj->addr)) < 0) {
disconnect(obj, "Error on bind()", 0); // TODO rendes hibaüzenet
return -1;
}
if (listen(obj->sock, 5)) {
disconnect(obj, "Error on listen()", 0);
return -1;
}
ev_io_set(&obj->w_in, obj->sock, EV_READ);
ev_io_set(&obj->w_out, obj->sock, EV_WRITE);
//~ ev_timer_set(&obj->w_connect_timeout, (float)obj->connect_timeout / 1000, 0);
ev_io_start(obj->loop, &obj->w_in);
ev_io_start(obj->loop, &obj->w_out);
//~ ev_timer_start(EV_DEFAULT, &obj->w_connect_timeout);
return 0;
}
/**
* @brief write data to netsocket socket
* @param obj the netsocket object
* @param data data string
* @param length length of data string
* @returns length of written data
*
* Write data to socket. O yeahhh:)
*/
int netsocket_write (netsocket_t *obj, const char *data, int length) {
int i = 0;
if (!obj->sock) return 0; // véletlenül sem írunk az stdout-ra :)
/* Úgy néz ki, hogy Linux alatt a send() az igazi. Az MSG_NOSIGNAL nélkül a
rendszer PIPE szignállal kinyírja a szervert abban az esetben, ha "Broken
pipe" állapot lép fel, tehát a socket bezárult (kliens megszakadt) de a
szerver még írni akar . Az MSG_NOSIGNAL állítólag csak Linux alatt
létezik. BSD alatt pl. az a megoldás, hogy a setsockopt függvénnyel be kell
állítani az adott socketen a SO_NOSIGPIPE flag-et, majd write() vagy más
függvénnyel lehet a socketre irkálni. További probléma a netsocket
szerkezetéből adódik, ahová mindig eljutok, amikor aszinkron rendszerben
meghívódik egy callback és a callback-en belül van törölve az az objektum,
ami a callback-et meghívta. Ez ugye paradoxon, avagy magad alatt vágod a
fát, C-ben pedig csúnya segfault vagy még rosszabb. Jelen esetben itt a
send() függvény visszatérési értékénél kellene ellenőrizni a "Broken pipe"
esetet és ha bekövetkezik, akkor a netsocket_disconnect() függvénnyel
megszakítani a kapcsolatot és hátrahagyni a "Broken pipe" üzenetet. Ebben az
esetben a netsocket_disconnect meghívja a sock_close függvényt, ami törli a
netsocket objektumot, majd visszatér ide. Ezen a ponton már a netsocket
objektum, amivel eddig dolgoztunk, nem létezik és elképzelhető, hogy érkezik
még egy netsocket_write függvényhívás vagy eleve mivel callback-ben vagyunk,
más is történik a netsocket-tel, ami már végzetes egy nem létező objektumon.
Erre kell kitalálni valami megoldást! Két ötlet van: vagy hardcore módon
minden netsocket függvény a meghívásnál leellenőrzi, hogy a paraméterül
átadott netsocket objektum létezik, írható, stb., és ha nem, akkor visszatér
hibával. A másik módszer pedig, hogy aszinkron módon történik a netsocket
objektumok törlése. A netsocket_disconnect megrendeli a törlést, és majd a
callback lefutása után ténylegesen le is lesz törölve. Ezt az aszinkron
megoldást már csináltam Perl-ben és jól működött, de valahogyan most ezt a
kommentet fogalmazva, a hardcore verzió tűnik a legjobbnak, tehát: minden
azonnal történjen meg, semmi ne blokkolódjon, ha már nem létezik az adott
objektum, akkor hibával térjünk vissza és ne folytassuk a műveletet. Ezt
minden olyan helyen ellenőrizni kell, ahol az objektummal munka van. Ez a
koncepció talán a szálaknál is alkalmazható egy-egy mutex lock körzet alatt.
Végiggondolni a szitut úgy, hogy egy callback-ből hívott netsocket_write
hívja meg a netsocket_disconnect-et:) Praktikusan az, ami miatt ezt a
litániát megírtam. */
/* Ötlet. Az aszinkron megoldást végiggondolni jobban! A netsocket_destroy()
meghívására ne törlődjön azonnal a netsocket. Ha callback-ben van éppen,
akkor a callback után történjen a felszabadítás. Vagy dupla mutatót használni
és ellenőrizni a NULL értéket? Faszság:) */
// i = write(obj->sock, data, length); // nem szignál-biztos
i = send(obj->sock, data, length, MSG_NOSIGNAL);
return i;
}
/**
* @brief Create a new netsocket object
* @param callback callback function pointer
* @param userdata user defined pointer
* @param loop event loop
* @returns a new netsocket object
*
* Create and return a malloc'ed netsocket object. Save callback and userdata
* in the netsocket structure. Callback will be called when an event occur. Userdata
* is an user defined pointer. If the optional loop parameter is given, then tell
* the netsocket to use this event loop for async operations. This is useful in
* multithreading environment. If the loop is NULL, then the default event loop
* will be used.
*/
netsocket_t *netsocket_new (void *callback, void *userdata, struct ev_loop *loop) {
netsocket_t *obj = malloc(sizeof(*obj));
if (obj == NULL)
return NULL;
//~ printf("netsocket object size = %d\n", sizeof(*obj));
bzero(obj, sizeof(*obj)); // mindent nullázunk
// ha meg van adva a loop paraméter, akkor azt használjuk eseménykezelőnek
// ellenkező esetben az alapértelmezett eseménykezelőt
obj->loop = (loop != NULL) ? loop : ev_default_loop(0);
// default értékek
obj->connect_timeout = 5000; // 5000 millisec
obj->callback = callback;
obj->userdata = userdata;
obj->w_in.data = obj;
obj->w_out.data = obj;
obj->w_connect_timeout.data = obj;
ev_init(&obj->w_in, (void*)w_in_cb);
ev_init(&obj->w_out, (void*)w_out_cb);
ev_init(&obj->w_connect_timeout, (void*)w_connect_timeout_cb);
return obj;
}
/**
* @brief destroy the netsocket object
* @param obj the netsocket object
*
* Disconnect from peer(s) without call the callback, close sockets,
* free all resources and destroy the netsocket object.
*/
// TODO ha a szerver netsocket-et szabadítjuk fel, akkor az összes hozzá tartozó
// kliens is menjen a levesbe
void netsocket_destroy (netsocket_t *obj) {
// ha nincs objektum, akkor lófaszjóska
if (obj == NULL)
return;
if (obj->in_callback > 0) {
obj->destroy_request = 1;
return;
}
disconnect(obj, NULL, 1);
free(obj);
}
// formázott konzol üzenet kiírása időbélyeggel
int netsocket_printf (netsocket_t *obj, const char *fmt, ...) {
char tmp[8192];
va_list ap;
va_start(ap, fmt);
vsnprintf(tmp, sizeof(tmp) - 1, fmt, ap);
va_end(ap);
return netsocket_write(obj, tmp, strlen(tmp));
}
void netsocket_host (netsocket_t *obj, const char *host) {
if (host != NULL)
strncpy(obj->host, host, sizeof(obj->host) - 1);
}
void netsocket_lhost (netsocket_t *obj, const char *lhost) {
if (lhost != NULL)
strncpy(obj->lhost, lhost, sizeof(obj->lhost) - 1);
}
void netsocket_port (netsocket_t *obj, int port) {
obj->port = port;
}
void netsocket_lport (netsocket_t *obj, int lport) {
obj->lport = lport;
}
int netsocket_is_connected (netsocket_t *obj) {
return obj->connected;
}

View File

@ -0,0 +1,78 @@
/* netsocket.h
* Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
#ifndef NETSOCKET_H_INCLUDED
#define NETSOCKET_H_INCLUDED
#include <netdb.h>
#include <ev.h>
#define NETSOCKET_IN 1
#define NETSOCKET_OUT 2
#define NETSOCKET_CLIENT 1
#define NETSOCKET_SERVER 2
#define NETSOCKET_TCP 1
#define NETSOCKET_UNIX 2
#define NETSOCKET_STATE_RESOLVING 1
#define NETSOCKET_STATE_CONNECTING 2
#define NETSOCKET_STATE_CONNECTED 3
#define NETSOCKET_EVENT_ERROR 1
#define NETSOCKET_EVENT_CONNECT 2
#define NETSOCKET_EVENT_READ 3
#define NETSOCKET_EVENT_DISCONNECT 4
typedef struct netsocket_t {
char host[128];
int port;
char lhost[128];
int lport;
int connect_timeout;
char ip[20];
int sock;
struct hostent *hostent;
struct sockaddr_in addr;
char inbuf[1024];
int inbuf_len;
ev_io w_out;
ev_io w_in;
ev_timer w_connect_timeout;
void (*callback)(void*, int);
void *userdata; // user data
int err;
int event;
char disconnect_reason[128];
int connected;
int mode;
struct netsocket_t *parent;
int direction;
int destroy_request;
int in_callback;
int disable_lookup_on_accept;
struct ev_loop *loop;
} netsocket_t;
netsocket_t *netsocket_new (void *callback, void *userdata, struct ev_loop *loop);
void netsocket_destroy (netsocket_t *obj);
int netsocket_connect (netsocket_t *obj);
int netsocket_listen (netsocket_t *obj);
void netsocket_disconnect (netsocket_t *obj, char *reason);
void netsocket_disconnect_withevent (netsocket_t *obj, char *reason);
int netsocket_write (netsocket_t *obj, const char *data, int length);
int netsocket_printf (netsocket_t *obj, const char *fmt, ...);
void netsocket_disable_lookup_on_accept (netsocket_t *obj);
void netsocket_host (netsocket_t *obj, const char *host);
void netsocket_lhost (netsocket_t *obj, const char *host);
void netsocket_port (netsocket_t *obj, int port);
void netsocket_lport (netsocket_t *obj, int port);
int netsocket_is_connected (netsocket_t *obj);
#endif // #ifndef NETSOCKET_H_INCLUDED

View File

@ -0,0 +1,200 @@
/* pipe.c
* Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ev.h>
#include <string.h>
#include "netsocket.h"
static char *lhost; // = "0.0.0.0";
static int lport; // = 1111;
static char *host; // = "irc.jss.hu";
static int port; // = 6667;
//~ static int i = 0;
int n = 0;
//~ static netsocket_t *cnetsocket; // client
static netsocket_t *snetsocket; // server
static ev_timer halfsec_timer;
//~ static ev_timer reconnect_timer;
static void halfsec_timeout (EV_P_ ev_timer *w, int revents) {
//~ if (i == 2) netsocket_disconnect(netsocket, "ötödik másodperc miatt");
//~ if (i == 7) netsocket_connect(netsocket, "pingoly", 5555);
//~ printf("timeout %d\n", n++);
}
//~ static void reconnect_timeout (EV_P_ ev_timer *w, int revents) {
//~ ev_timer_stop(EV_DEFAULT, &reconnect_timer);
//~ printf("Connecting [#%d] to %s:%d...\n", i++, host, port);
//~ netsocket_connect(cnetsocket, host, port);
//~ }
static void ccallback (netsocket_t *netsocket, int event) {
//~ printf("Callback event = %d - %s\n", event, netsocket->disconnect_reason);
switch (event) {
case NETSOCKET_EVENT_CONNECT:
printf("Connected to %s (%s) port %d\n",
netsocket->host,
netsocket->ip,
netsocket->port
);
//~ char *msg = "Szevasz szerver!\n";
//~ netsocket_write(netsocket, msg, strlen(msg));
break;
case NETSOCKET_EVENT_DISCONNECT:
//~ ev_timer_again(EV_DEFAULT, &reconnect_timer);
//~ ev_timer_start(EV_DEFAULT, &reconnect_timer);
if (netsocket->connected) {
printf("Disconnected from %s: %s\n",
netsocket->host,
netsocket->disconnect_reason
);
} else {
printf("Can't connect to %s: %s\n",
netsocket->host,
netsocket->disconnect_reason
);
}
netsocket_destroy(netsocket->userdata);
netsocket_destroy(netsocket);
break;
case NETSOCKET_EVENT_READ:
printf("Got %d bytes from %s:%d\n",
netsocket->inbuf_len,
netsocket->host,
netsocket->port
);
//~ printf("inbuf = %s\n", netsocket->inbuf);
netsocket_write(netsocket->userdata, netsocket->inbuf, netsocket->inbuf_len);
break;
default:
printf("Unhandled event: %d\n", event);
break;
}
//~ return;
//~ printf("Callback jott: %s\nHost: %s\nIP: %s\nErr: %s\n",
//~ (char*)netsocket->userdata, netsocket->host, netsocket->ip, strerror(netsocket->err));
}
static void scallback (netsocket_t *netsocket, int event) {
//~ printf("Callback event = %d - %s\n", event, netsocket->disconnect_reason);
switch (event) {
case NETSOCKET_EVENT_CONNECT:
// a netsocket az új kliens socket
// a szülőt, tehát azt a netsocket objektumot, ami a szerver
// és figyel a porton, azt a netsocket->parent lehet elérni
// a netsocket->userdata megegyezik a szülő userdata-val
printf("Connect from %s:%d (%s)\n",
netsocket->ip,
netsocket->port,
netsocket->host
);
//~ char *msg = "Szevasz kliens!\n";
//~ netsocket_write(netsocket, msg, strlen(msg));
netsocket_t *newnetsocket;
newnetsocket = netsocket_new(ccallback, netsocket, NULL);
netsocket_host(newnetsocket, host);
netsocket_port(newnetsocket, port);
netsocket->userdata = newnetsocket;
netsocket_connect(netsocket->userdata);
//~ netsocket_disconnect(netsocket, "megszakadsz");
//~ netsocket_destroy(netsocket);
break;
case NETSOCKET_EVENT_DISCONNECT:
//~ ev_timer_again(EV_DEFAULT, &reconnect_timer);
//~ ev_timer_start(EV_DEFAULT, &reconnect_timer);
if (netsocket->connected) {
printf("Disconnected from %s: %s\n",
netsocket->host,
netsocket->disconnect_reason
);
netsocket_destroy(netsocket->userdata);
netsocket_destroy(netsocket);
} else {
printf("Can't listen on %s:%d: %s\n",
netsocket->host,
netsocket->port,
netsocket->disconnect_reason
);
exit(255);
}
break;
case NETSOCKET_EVENT_READ:
printf("Got %d bytes from %s:%d\n",
netsocket->inbuf_len,
netsocket->host,
netsocket->port
);
//~ printf("inbuf = %s\n", netsocket->inbuf);
netsocket_write(netsocket->userdata, netsocket->inbuf, netsocket->inbuf_len);
break;
default:
printf("Unhandled event: %d\n", event);
break;
}
//~ return;
//~ printf("Callback jott: %s\nHost: %s\nIP: %s\nErr: %s\n",
//~ (char*)netsocket->userdata, netsocket->host, netsocket->ip, strerror(netsocket->err));
}
int main (int argc, char **argv) {
if (argc < 5) {
printf("usage: %s <local host> <local port> <remote host> <remote port>\n", argv[0]);
exit(255);
}
lhost = argv[1];
lport = atoi(argv[2]);
host = argv[3];
port = atoi(argv[4]);
//~ cnetsocket = netsocket_new(ccallback, "juzerdata");
//~ netsocket_connect(netsocket, "pingoly", 5555);
//~ netsocket_destroy(netsocket);
snetsocket = netsocket_new(scallback, "ez itt a szerver", EV_DEFAULT);
netsocket_lhost(snetsocket, lhost);
netsocket_lport(snetsocket, lport);
if (netsocket_listen(snetsocket)) {
perror("netsocket_listen");
exit(255);
}
ev_timer_init(&halfsec_timer, halfsec_timeout, 0.5, 0.5);
ev_timer_start(EV_DEFAULT, &halfsec_timer);
// azonnali reconnect, majd disconnect esetén 1.5 másodperc múlva
//~ ev_timer_init(&reconnect_timer, reconnect_timeout, 0., 1.5);
//~ ev_timer_start(EV_DEFAULT, &reconnect_timer);
ev_loop(EV_DEFAULT, 0);
return 0;
}

View File

@ -0,0 +1,181 @@
/* test.c
* Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ev.h>
#include <string.h>
#include "netsocket.h"
static char *lhost = "0.0.0.0";
static int lport = 1111;
static char *host;
static int port;
static int i = 0;
int n = 0;
static netsocket_t *cnetsocket; // client
static netsocket_t *snetsocket; // server
static ev_timer halfsec_timer;
static ev_timer reconnect_timer;
static void halfsec_timeout (EV_P_ ev_timer *w, int revents) {
//~ if (i == 2) netsocket_disconnect(netsocket, "ötödik másodperc miatt");
//~ if (i == 7) netsocket_connect(netsocket, "pingoly", 5555);
//~ printf("timeout %d\n", n++);
}
static void reconnect_timeout (EV_P_ ev_timer *w, int revents) {
ev_timer_stop(EV_DEFAULT, &reconnect_timer);
printf("Connecting [#%d] to %s:%d...\n", i++, host, port);
netsocket_connect(cnetsocket, host, port);
}
static void ccallback (netsocket_t *netsocket, int event) {
//~ printf("Callback event = %d - %s\n", event, netsocket->disconnect_reason);
switch (event) {
case NETSOCKET_EVENT_CONNECT:
printf("Connected to %s (%s) port %d\n",
netsocket->host,
netsocket->ip,
netsocket->port
);
char *msg = "Szevasz szerver!\n";
netsocket_write(netsocket, msg, strlen(msg));
break;
case NETSOCKET_EVENT_DISCONNECT:
//~ ev_timer_again(EV_DEFAULT, &reconnect_timer);
ev_timer_start(EV_DEFAULT, &reconnect_timer);
if (netsocket->connected) {
printf("Disconnected from %s: %s\n",
netsocket->host,
netsocket->disconnect_reason
);
} else {
printf("Can't connect to %s: %s\n",
netsocket->host,
netsocket->disconnect_reason
);
}
break;
case NETSOCKET_EVENT_READ:
printf("Got %d bytes from %s:%d\n",
netsocket->inbuf_len,
netsocket->host,
netsocket->port
);
printf("inbuf = %s\n", netsocket->inbuf);
break;
default:
printf("Unhandled event: %d\n", event);
break;
}
//~ return;
//~ printf("Callback jott: %s\nHost: %s\nIP: %s\nErr: %s\n",
//~ (char*)netsocket->userdata, netsocket->host, netsocket->ip, strerror(netsocket->err));
}
static void scallback (netsocket_t *netsocket, int event) {
//~ printf("Callback event = %d - %s\n", event, netsocket->disconnect_reason);
switch (event) {
case NETSOCKET_EVENT_CONNECT:
// a netsocket az új kliens socket
// a szülőt, tehát azt a netsocket objektumot, ami a szerver
// és figyel a porton, azt a netsocket->parent lehet elérni
// a netsocket->userdata megegyezik a szülő userdata-val
printf("Connect from %s:%d (%s)\n",
netsocket->ip,
netsocket->port,
netsocket->host
);
char *msg = "Szevasz kliens!\n";
netsocket_write(netsocket, msg, strlen(msg));
//~ netsocket_disconnect(netsocket, "megszakadsz");
//~ netsocket_destroy(netsocket);
break;
case NETSOCKET_EVENT_DISCONNECT:
//~ ev_timer_again(EV_DEFAULT, &reconnect_timer);
//~ ev_timer_start(EV_DEFAULT, &reconnect_timer);
if (netsocket->connected) {
printf("Disconnected from %s: %s\n",
netsocket->host,
netsocket->disconnect_reason
);
netsocket_destroy(netsocket);
} else {
printf("Can't listen on %s:%d: %s\n",
netsocket->host,
netsocket->port,
netsocket->disconnect_reason
);
}
break;
case NETSOCKET_EVENT_READ:
printf("Got %d bytes from %s:%d\n",
netsocket->inbuf_len,
netsocket->host,
netsocket->port
);
printf("inbuf = %s\n", netsocket->inbuf);
break;
default:
printf("Unhandled event: %d\n", event);
break;
}
//~ return;
//~ printf("Callback jott: %s\nHost: %s\nIP: %s\nErr: %s\n",
//~ (char*)netsocket->userdata, netsocket->host, netsocket->ip, strerror(netsocket->err));
}
int main (int argc, char **argv) {
if (argc < 3) {
printf("usage: %s <host> <port>\n", argv[0]);
exit(255);
}
host = argv[1];
port = atoi(argv[2]);
cnetsocket = netsocket_new(ccallback, "juzerdata");
//~ netsocket_connect(netsocket, "pingoly", 5555);
//~ netsocket_destroy(netsocket);
snetsocket = netsocket_new(scallback, "ez itt a szerver");
if (netsocket_listen(snetsocket, lhost, lport)) {
perror("netsocket_listen");
exit(255);
}
ev_timer_init(&halfsec_timer, halfsec_timeout, 0.5, 0.5);
ev_timer_start(EV_DEFAULT, &halfsec_timer);
// azonnali reconnect, majd disconnect esetén 1.5 másodperc múlva
ev_timer_init(&reconnect_timer, reconnect_timeout, 0., 1.5);
//~ ev_timer_start(EV_DEFAULT, &reconnect_timer);
ev_loop(EV_DEFAULT, 0);
return 0;
}

View File

@ -0,0 +1,11 @@
CC = gcc
CFLAGS = -Wall -I.. -ggdb
LDFLAGS = -lpthread
all: test_tracer
test_tracer: test_tracer.o tracer.o
$(CC) $(CFLAGS) -o test_tracer test_tracer.o tracer.o $(LDFLAGS)
clean:
rm -f test_tracer *.o

View File

@ -0,0 +1,33 @@
/* test_tracer.c
* Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include "tracer.h"
#include "debug.h"
int main (int argc, const char *argv[]) {
tracer_t *tra = tracer_new("proba");
if (tra == NULL) {
printf("out of memory\n");
return -1;
}
tracer_open(tra);
tracer_printf(tra, "hello %d\n", 59);
tracer_printf(tra, "hallo %d\n", 61);
printf("lezárva: %llu\n", tracer_close(tra));
tracer_destroy(tra);
return -1;
}

View File

@ -0,0 +1,185 @@
/* tracer.c - simple trace file handling for debugging
* Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "debug.h"
#include "tracer.h"
#define TRACER_DEFAULT_START_ID 10000
// A tra->id -be rak egy új id-t, amit a tra->seqfile alapján inkrementál és
// ment el. Ha nem létezik a seq fájl vagy faszság van benne, akkor
// tra->start_id értéke kerül a tra->id -be, és ezt is menti el a seq fájlba.
//
// visszatérési érték:
// 0 = sikeres volt új id-t generálni és elmenteni
// -1 = valami grimbusz volt
static int new_id (tracer_t *tra) {
// ha nem létezik még a tra->seqpath fájl, akkor legyártjuk 644-es joggal,
// mert különben az fopen "r+" NULL-al tér vissza
if (access(tra->seqpath, F_OK)) {
int fd = open(tra->seqpath, O_RDWR | O_CREAT, 0644);
close(fd);
}
FILE *file = fopen(tra->seqpath, "r+");
if (file == NULL) {
printf("Can't open %s: %s\n", tra->seqpath, strerror(errno));
return -1;
}
// seq fájl zárolása
flock(fileno(file), LOCK_EX);
unsigned long long id = 0;
fscanf(file, "%llu\n", &id);
id = (id == 0) ? tra->start_id : id + 1;
rewind(file);
fprintf(file, "%llu\n", id);
// seq fájl elengedése
flock(fileno(file), LOCK_UN);
fclose(file);
tra->id = id;
return 0;
}
// visszatérési érték:
// 0 = létezik a könyvtár
// -1 = nem létezik a könyvtár
static int test_directory_exists (const char *path) {
DIR *dir = opendir(path);
if (dir) {
closedir(dir);
return 0;
}
return -1;
}
// Létrehoz egy új tracer objektumot. Paraméterül kap egy könyvtár nevet, ahova
// a tracer fájlokat fogja menteni a tracer_open(), tracer_printf() és
// tracer_close(). A könyvtárnév lehet relatív elérési útvonal is. A
// tracer_new() a realpath() rendszerhívás segítségével megállapítja az
// abszolút útvonalat (/var/log/....) és a továbbiakban ezt fogja használni.
// Ezáltal a programban tetszés szerint lehet chdir() hívásokat elhelyezni.
// Azok nem fogják befolyásolni a tracert.
tracer_t *tracer_new (const char *directory) {
tracer_t *tra = malloc(sizeof(*tra));
if (tra == NULL) {
fprintf(stderr, "tracer_new(): out of memory");
return NULL;
}
memset(tra, 0, sizeof(*tra));
// relatív útvonalat menti el a tra->path -ba
realpath(directory, tra->path);
// tra->path könyvtár készítése 755 jogokkal
mkdir(tra->path, 0755);
// ha nem létezik a könyvtár, akkor grimbusz van és NULL-ra állítjuk a
// path-ot, aminek hatására a tracer_open() nem fog dolgozni
if (test_directory_exists(tra->path)) {
tra->path[0] = '\0';
}
// sorszám-fájl elérési útjának tárolása
snprintf(tra->seqpath, sizeof(tra->seqpath), "%s/.seq", tra->path);
// default értékek
tra->start_id = TRACER_DEFAULT_START_ID; // ezt írja felül a tracer_set_start_id()
return tra;
}
void tracer_open (tracer_t *tra) {
if (tra == NULL || tra->path[0] == '\0')
return;
// ha már van nyitott fájlunk, akkor azt először bezárjuk
if (tra->file != NULL)
tracer_close(tra);
// új tra->id generálása seq fájlból. Ha nem sikerül, akkor return-al
// visszatérünk
if (new_id(tra))
return;
// fájl teljes elérési útjának elkészítése
snprintf(tra->filepath, sizeof(tra->filepath), "%s/%llu", tra->path, tra->id);
// megnyitás írásra
tra->file = fopen(tra->filepath, "w");
}
unsigned long long tracer_close (tracer_t *tra) {
if (tra == NULL || tra->file == NULL)
return 0;
fclose(tra->file);
tra->file = NULL;
unsigned long long tmp = tra->id;
tra->id = 0;
return tmp;
}
void tracer_printf (tracer_t *tra, const char *fmt, ...) {
if (tra == NULL || tra->file == NULL)
return;
va_list ap;
va_start(ap, fmt);
vfprintf(tra->file, fmt, ap);
va_end(ap);
}
void tracer_destroy (tracer_t *tra) {
if (tra == NULL)
return;
tracer_close(tra);
free(tra);
}
void tracer_set_start_id (tracer_t *tra, int start_id) {
if (tra == NULL)
return;
tra->start_id = start_id;
}
unsigned long long tracer_get_id (tracer_t *tra) {
if (tra == NULL)
return 0;
return tra->id;
}
char *tracer_get_id_as_string (tracer_t *tra) {
if (tra == NULL)
return 0;
snprintf(tra->id_as_string, sizeof(tra->id_as_string), "%llu", tra->id);
return tra->id_as_string;
}

View File

@ -0,0 +1,110 @@
/* tracer.h - simple trace file handling for debugging
* Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*
SYNOPSIS
--------
tracer_t *tra = tracer_new("MyTraceLogFolder");
if (tra == NULL) {
printf("out of memory\n");
return -1;
}
// first file
tracer_open(tra);
tracer_printf(tra, "blahhh\n");
tracer_close(tra);
// secondary file
tracer_open(tra);
tracer_printf(tra, "hello %d\n", 59);
tracer_printf(tra, "hallo %d\n", 61);
// you can save this ID to your logs for later examination
unsigned long long id;
id = tracer_get_id(tra);
// close and retrieve ID in one step
printf("closed, tracer id = %llu\n", tracer_close(tra));
// close and free all resources
tracer_destroy(tra);
DESCRIPTION
-----------
A tracer elve az, hogy a program (démon) futása során tetszőleges,
nagyobb mennyiségű információt gyűjthessünk össze debuggolás céljából
úgy, hogy az ne szemetelje tele a konzolt vagy a log fájlokat. Ezek a
debug infók sorszámozott szöveges fájlokba kerülnek, majd a fájlok
sorszámára lehet hivatkozni a logban, így utólag könnyen kikereshető egy
esemény részletes körülményképe. Ráadásul egy `find -mtime` paranccsal
gyorsan meg lehet szabadnulni a régi, fölösleges tracelogoktól.
*/
#include <limits.h>
typedef struct tracer_t {
unsigned long long id;
char id_as_string[32];
unsigned long long start_id;
FILE *file;
char path[PATH_MAX]; // realpath() függvény ide rakja az abszolút elérési utat
char filepath[PATH_MAX]; // path + fájlnév kerül ide (tracer adat fájl)
char seqpath[PATH_MAX]; // path + fájlnév kerül ide (sorszám fájl)
} tracer_t;
// új tracer objektum, melynek munkakönyvtára a directory-ban megadott abszolút
// vagy relatív elérési útvonal. A tracer_new() a realpath() rendszerhívás
// segítségével megállapítja az abszolút útvonalat (/var/log/....) és a
// továbbiakban ezt fogja használni. Ezáltal a programban tetszés szerint lehet
// chdir() hívásokat elhelyezni, mert azok nem fogják befolyásolni a tracert.
tracer_t *tracer_new (const char *directory);
// Megnyitja írásra a sorban következő új fájlt. A fájl sorszámát a
// munkakönyvtárban található .seq fájl alapján inkrementálja. Ha a .seq nem
// létezik, akkor a tracer_set_start_id() függvénnyel beállított érték lesz a
// kezdő sorszám. Ennek alapértelmezett értéke TRACER_DEFAULT_START_ID. Ha a
// tracer_new() nem tudja beállítani vagy létrehozni a munkakönyvtárat,
// akkor a tracer_open() nem csinál semmit. A .seq fájl kezelése szál-biztos és
// processz-biztos, mert flock() által van zárolva.
void tracer_open (tracer_t *tra);
// Formázott szöveget ír a már megnyitott fájlba. Ha nincs megnyitva a fájl,
// akkor nem csinál semmit.
void tracer_printf (tracer_t *tra, const char *fmt, ...);
// Lezárja a tracer fájlt és visszaadja a sorszámát, ugyan azt, amit a
// tracer_get_id() ad vissza.
unsigned long long tracer_close (tracer_t *tra);
// Ha van, akkor lezárja az éppen megnyitott fájlt és felszabadítja a tracer
// objektum által lefoglalt memóriát. Ha a paraméterül NULL-t kap, nem csinál
// semmit.
void tracer_destroy (tracer_t *tra);
// Beállítja a kezdő sorszámot, mely akkor lesz felhasználva, ha nincs .seq
// fájl. Az alapértelmezett érték TRACER_DEFAULT_START_ID.
void tracer_set_start_id (tracer_t *tra, int start_id);
// Visszaadja az éppen használt fájl sorszámát. Ha a hívás pillanatában nincs
// nyitva fájl, tehát nem egy tracer_open() és egy tracer_close() között
// vagyunk, akkor a visszatérési érték "0".
unsigned long long tracer_get_id (tracer_t *tra);
// String-ként adja vissza az éppen használt fájl sorszámát. Ha a hívás
// pillanatában nincs nyitva fájl, tehát nem egy tracer_open() és egy
// tracer_close() között vagyunk, akkor a visszatérési érték "0".
char *tracer_get_id_as_string (tracer_t *tra);

View File

@ -0,0 +1,502 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View File

@ -0,0 +1,197 @@
#! /usr/bin/make -rf
# Makefile.in
# libudns Makefile
#
# Copyright (C) 2005 Michael Tokarev <mjt@corpit.ru>
# This file is part of UDNS library, an async DNS stub resolver.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library, in file named COPYING.LGPL; if not,
# write to the Free Software Foundation, Inc., 59 Temple Place,
# Suite 330, Boston, MA 02111-1307 USA
NAME = udns
VERS = 0.4
SOVER = 0
SRCS = udns_dn.c udns_dntosp.c udns_parse.c udns_resolver.c udns_init.c \
udns_misc.c udns_XtoX.c \
udns_rr_a.c udns_rr_ptr.c udns_rr_mx.c udns_rr_txt.c udns_bl.c \
udns_rr_srv.c udns_rr_naptr.c udns_codes.c udns_jran.c
USRCS = dnsget.c rblcheck.c ex-rdns.c
DIST = COPYING.LGPL udns.h udns.3 dnsget.1 rblcheck.1 $(SRCS) $(USRCS) \
NEWS TODO NOTES Makefile.in configure configure.lib \
inet_XtoX.c getopt.c
OBJS = $(SRCS:.c=.o) $(GEN:.c=.o)
LIB = lib$(NAME).a
LIBFL = -L. -l$(NAME)
SOBJS = $(OBJS:.o=.lo)
SOLIB = lib$(NAME)_s.so
SOLIBV = lib$(NAME).so.$(SOVER)
SOLIBFL= -L. -l$(NAME)_s
UTILS = $(USRCS:.c=)
UOBJS = $(USRCS:.c=.o)
SOUTILS = $(USRCS:.c=_s)
NAMEPFX = $(NAME)-$(VERS)
CC = @CC@
CFLAGS = @CFLAGS@
CDEFS = @CDEFS@
LD = @LD@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
LDSHARED = $(LD) -shared
PICFLAGS = -fPIC
AWK = awk
TAR = tar
all: static
.SUFFIXES: .c .o .lo
static: $(LIB) $(UTILS)
staticlib: $(LIB)
$(LIB): $(OBJS)
-rm -f $@
$(AR) rv $@ $(OBJS)
.c.o:
$(CC) $(CFLAGS) $(CDEFS) -c $<
shared: $(SOLIBV) $(SOUTILS)
sharedlib: $(SOLIBV)
$(SOLIBV): $(SOBJS)
$(LDSHARED) -Wl,--soname,$(SOLIBV) -o $@ $(SOBJS) $(LDFLAGS) $(LIBS)
$(SOLIB): $(SOLIBV)
rm -f $@
ln -s $(SOLIBV) $@
.c.lo:
$(CC) $(CFLAGS) $(PICFLAGS) $(CDEFS) -o $@ -c $<
# udns_codes.c is generated from udns.h
udns_codes.c: udns.h
@echo Generating $@
@set -e; exec >$@.tmp; \
set T type C class R rcode; \
echo "/* Automatically generated. */"; \
echo "#include \"udns.h\""; \
while [ "$$1" ]; do \
echo; \
echo "const struct dns_nameval dns_$${2}tab[] = {"; \
$(AWK) "/^ DNS_$${1}_[A-Z0-9_]+[ ]*=/ \
{ printf \" {%s,\\\"%s\\\"},\\n\", \$$1, substr(\$$1,7) }" \
udns.h ; \
echo " {0,0}};"; \
echo "const char *dns_$${2}name(enum dns_$${2} code) {"; \
echo " static char nm[20];"; \
echo " switch(code) {"; \
$(AWK) "BEGIN{i=0} \
/^ DNS_$${1}_[A-Z0-9_]+[ ]*=/ \
{printf \" case %s: return dns_$${2}tab[%d].name;\\n\",\$$1,i++}\
" udns.h ; \
echo " }"; \
echo " return _dns_format_code(nm,\"$$2\",code);"; \
echo "}"; \
shift 2; \
done
@mv $@.tmp $@
udns.3.html: udns.3
groff -man -Thtml udns.3 > $@.tmp
mv $@.tmp $@
dist: $(NAMEPFX).tar.gz
$(NAMEPFX).tar.gz: $(DIST)
$(TAR) -cv -f $@ -z --transform 's|^|$(NAMEPFX)/|' $(DIST)
subdist:
cp -p $(DIST) $(TARGET)/
clean:
rm -f $(OBJS)
rm -f $(SOBJS)
rm -f $(UOBJS)
rm -f config.log
distclean: clean
rm -f $(LIB) $(SOLIB) $(SOLIBV) udns.3.html
rm -f $(UTILS) $(SOUTILS)
rm -f config.status config.h Makefile
Makefile: configure configure.lib Makefile.in
./configure
@echo
@echo Please rerun make >&2
@exit 1
.PHONY: all static staticlib shared sharedlib dist clean distclean subdist \
depend dep deps
depend dep deps: $(SRCS) $(USRC)
@echo Generating deps for:
@echo \ $(SRCS)
@echo \ $(USRCS)
@sed '/^# depend/q' Makefile.in > Makefile.tmp
@set -e; \
for f in $(SRCS) $(USRCS); do \
echo $${f%.c}.o $${f%.c}.lo: $$f \
`sed -n 's/^#[ ]*include[ ]*"\(.*\)".*/\1/p' $$f`; \
done >> Makefile.tmp; \
for f in $(USRCS:.c=.o); do \
echo "$${f%.?}: $$f \$$(LIB)"; \
echo " \$$(LD) \$$(LDLAGS) -o \$$@ $$f \$$(LIBFL) \$$(LIBS)"; \
echo "$${f%.?}_s: $$f \$$(SOLIB)"; \
echo " \$$(LD) \$$(LDFLAGS) -o \$$@ $$f \$$(SOLIBFL)"; \
done >> Makefile.tmp ; \
if cmp Makefile.tmp Makefile.in >/dev/null 2>&1 ; then \
echo Makefile.in unchanged; rm -f Makefile.tmp; \
else \
echo Updating Makfile.in; mv -f Makefile.tmp Makefile.in ; \
fi
# depend
udns_dn.o udns_dn.lo: udns_dn.c udns.h
udns_dntosp.o udns_dntosp.lo: udns_dntosp.c udns.h
udns_parse.o udns_parse.lo: udns_parse.c udns.h
udns_resolver.o udns_resolver.lo: udns_resolver.c config.h udns.h
udns_init.o udns_init.lo: udns_init.c config.h udns.h
udns_misc.o udns_misc.lo: udns_misc.c udns.h
udns_XtoX.o udns_XtoX.lo: udns_XtoX.c config.h udns.h inet_XtoX.c
udns_rr_a.o udns_rr_a.lo: udns_rr_a.c udns.h
udns_rr_ptr.o udns_rr_ptr.lo: udns_rr_ptr.c udns.h
udns_rr_mx.o udns_rr_mx.lo: udns_rr_mx.c udns.h
udns_rr_txt.o udns_rr_txt.lo: udns_rr_txt.c udns.h
udns_bl.o udns_bl.lo: udns_bl.c udns.h
udns_rr_srv.o udns_rr_srv.lo: udns_rr_srv.c udns.h
udns_rr_naptr.o udns_rr_naptr.lo: udns_rr_naptr.c udns.h
udns_codes.o udns_codes.lo: udns_codes.c udns.h
udns_jran.o udns_jran.lo: udns_jran.c udns.h
dnsget.o dnsget.lo: dnsget.c config.h udns.h getopt.c
rblcheck.o rblcheck.lo: rblcheck.c config.h udns.h getopt.c
ex-rdns.o ex-rdns.lo: ex-rdns.c udns.h
dnsget: dnsget.o $(LIB)
$(LD) $(LDLAGS) -o $@ dnsget.o $(LIBFL) $(LIBS)
dnsget_s: dnsget.o $(SOLIB)
$(LD) $(LDFLAGS) -o $@ dnsget.o $(SOLIBFL)
rblcheck: rblcheck.o $(LIB)
$(LD) $(LDLAGS) -o $@ rblcheck.o $(LIBFL) $(LIBS)
rblcheck_s: rblcheck.o $(SOLIB)
$(LD) $(LDFLAGS) -o $@ rblcheck.o $(SOLIBFL)
ex-rdns: ex-rdns.o $(LIB)
$(LD) $(LDLAGS) -o $@ ex-rdns.o $(LIBFL) $(LIBS)
ex-rdns_s: ex-rdns.o $(SOLIB)
$(LD) $(LDFLAGS) -o $@ ex-rdns.o $(SOLIBFL)

View File

@ -0,0 +1,136 @@
NEWS
User-visible changes in udns library. Recent changes on top.
0.4 (Jan 2014)
- bugfix: fix a bug in new list code introduced in 0.3
- portability: use $(LD)/$(LDFLAGS)/$(LIBS)
0.3 (Jan 2014)
- bugfix: refactor double-linked list implementation in udns_resolver.c
(internal to the library) to be more strict-aliasing-friendly, because
old code were miscompiled by gcc.
- bugfix: forgotten strdup() in rblcheck
0.2 (Dec 2011)
- bugfix: SRV RR handling: fix domain name parsing and crash in case
if no port is specified on input for SRV record query
- (trivial api) dns_set_opts() now returns number of unrecognized
options instead of always returning 0
- dnsget: combine -f and -o options in dnsget (and stop documenting -f),
and report unknown/invalid -o options (and error out)
- dnsget: pretty-print SSHFP RRs
0.1 (Dec 2010)
- bugfix: udns_new(old) - when actually cloning another context -
makes the new context referencing memory from old, which leads
to crashes when old is modified later
- use random queue IDs (the 16bit qID) in queries instead of sequentional
ones, based on simple pseudo-random RNG by Bob Jenkins (udns_jran.[ch]).
Some people believe that this improves security (CVE-2008-1447). I'm
still not convinced (see comments in udns_resolver.c), but it isn't
difficult to add after all.
- deprecate dns_random16() function which was declared in udns.h
(not anymore) but never documented. In order to keep ABI compatible
it is still exported.
- library has a way now to set query flags (DNS_SET_DO; DNS_SET_CD).
- dnsget now prints non-printable chars in all strings in DNS RRs using
decimal escape sequences (\%03u) instead of hexadecimal (\%02x) when
before - other DNS software does it like this.
- recognize a few more record types in dnsget, notable some DNSSEC RRs;
add -f option for dnsget to set query flags.
- udns is not a Debian native package anymore (was a wrong idea)
0.0.9 (16 Jan 2007)
- incompat: minor API changes in dns_init() &friends. dns_init()
now requires extra `struct dns_ctx *' argument. Not bumped
soversion yet - I only expect one "release" with this change.
- many small bugfixes, here and there
- more robust FORMERR replies handling - not only such replies are now
recognized, but udns retries queries without EDNS0 extensions if tried
with, but server reported FORMERR
- portability changes, udns now includes getopt() implementation fo
the systems lacking it (mostly windows), and dns_ntop()&dns_pton(),
which are either just wrappers for system functions or reimplementations.
- build is now based on autoconf-like configuration
- NAPTR (RFC3403) RR decoding support
- new file NOTES which complements TODO somewhat, and includes some
important shortcomings
- many internal cleanups, including some preparations for better error
recovery, security and robustness (and thus API changes)
- removed some #defines which are now unused (like DNS_MAXSRCH)
- changed WIN32 to WINDOWS everywhere in preprocessor tests,
to be able to build it on win64 as well
0.0.8 (12 Sep 2005)
- added SRV records (rfc2782) parsing,
thanks to Thadeu Lima de Souza Cascardo for implementation.
- bugfixes:
o use uninitialized value when no reply, library died with assertion:
assert((status < 0 && result == 0) || (status >= 0 && result != 0)).
o on some OSes, struct sockaddr_in has additional fields, so
memcmp'ing two sockaddresses does not work.
- rblcheck(.1)
0.0.7 (20 Apr 2005)
- dnsget.1 manpage and several enhancements to dnsget.
- allow nameserver names for -n option of dnsget.
- API change: all dns_submit*() routines now does not expect
last `now' argument, since requests aren't sent immediately
anymore.
- API change: different application timer callback mechanism.
Udns now uses single per-context timer instead of per-query.
- don't assume DNS replies only contain backward DN pointers,
allow forward pointers too. Change parsing API.
- debianize
0.0.6 (08 Apr 2005)
- use double sorted list for requests (sorted by deadline).
This should significantly speed up timeout processing for
large number of requests.
- changed debugging interface, so it is finally useable
(still not documented).
- dnsget routine is now Officially Useable, and sometimes
even more useable than `host' from BIND distribution
(and sometimes not - dnsget does not have -C option
and TCP mode)
- Debian packaging in debian/ -- udns is now maintained as a
native Debian package.
- alot (and I really mean alot) of code cleanups all over.

View File

@ -0,0 +1,226 @@
Assorted notes about udns (library).
UDP-only mode
~~~~~~~~~~~~~
First of all, since udns is (currently) UDP-only, there are some
shortcomings.
It assumes that a reply will fit into a UDP buffer. With adoption of EDNS0,
and general robustness of IP stacks, in most cases it's not an issue. But
in some cases there may be problems:
- if an RRset is "very large" so it does not fit even in buffer of size
requested by the library (current default is 4096; some servers limits
it further), we will not see the reply, or will only see "damaged"
reply (depending on the server).
- many DNS servers ignores EDNS0 option requests. In this case, no matter
which buffer size udns library will request, such servers reply is limited
to 512 bytes (standard pre-EDNS0 DNS packet size). (Udns falls back to
non-EDNO0 query if EDNS0-enabled one received FORMERR or NOTIMPL error).
The problem is that with this, udns currently will not consider replies with
TC (truncation) bit set, and will treat such replies the same way as it
treats SERVFAIL replies, thus trying next server, or temp-failing the query
if no more servers to try. In other words, if the reply is really large, or
if the servers you're using don't support EDNS0, your application will be
unable to resolve a given name.
Yet it's not common situation - in practice, it's very rare.
Implementing TCP mode isn't difficult, but it complicates API significantly.
Currently udns uses only single UDP socket (or - maybe in the future - two,
see below), but in case of TCP, it will need to open and close sockets for
TCP connections left and right, and that have to be integrated into an
application's event loop in an easy and efficient way. Plus all the
timeouts - different for connect(), write, and several stages of read.
IPv6 vs IPv4 usage
~~~~~~~~~~~~~~~~~~
This is only relevant for nameservers reachable over IPv6, NOT for IPv6
queries. I.e., if you've IPv6 addresses in 'nameservers' line in your
/etc/resolv.conf file. Even more: if you have BOTH IPv6 AND IPv4 addresses
there. Or pass them to udns initialization routines.
Since udns uses a single UDP socket to communicate with all nameservers,
it should support both v4 and v6 communications. Most current platforms
supports this mode - using PF_INET6 socket and V4MAPPED addresses, i.e,
"tunnelling" IPv4 inside IPv6. But not all systems supports this. And
more, it has been said that such mode is deprecated.
So, list only IPv4 or only IPv6 addresses, but don't mix them, in your
/etc/resolv.conf.
An alternative is to use two sockets instead of 1 - one for IPv6 and one
for IPv4. For now I'm not sure if it's worth the complexity - again, of
the API, not the library itself (but this will not simplify library either).
Single socket for all queries
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Using single UDP socket for sending queries to all nameservers has obvious
advantages. First it's, again, trivial, simple to use API. And simple
library too. Also, after sending queries to all nameservers (in case first
didn't reply in time), we will be able to receive late reply from first
nameserver and accept it.
But this mode has disadvantages too. Most important is that it's much easier
to send fake reply to us, as the UDP port where we expects the reply to come
to is constant during the whole lifetime of an application. More secure
implementations uses random port for every single query. While port number
(16 bits integer) can not hold much randomness, it's still of some help.
Ok, udns is a stub resolver, so it expects sorta friendly environment, but
on LAN it's usually much easier to fire an attack, due to the speed of local
network, where a bad guy can generate alot of packets in a short time.
Spoofing of replies (Kaminsky attack, CVE-2008-1447)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
While udns uses random numbers for query IDs, it uses single UDP port for
all queries (see previous item). And even if it used random UDP port for
each query, the attack described in CVE-2008-1447 is still quite trivial.
This is not specific to udns library unfortunately - it is inherent property
of the protocol. Udns is designed to work in a LAN, it needs full recursive
resolver nearby, and modern LAN usually uses high-bandwidth equipment which
makes the Kaminsky attack trivial. The problem is that even with qID (16
bits) and random UDP port (about 20 bits available to a regular process)
combined still can not hold enough randomness, so on a fast network it is
still easy to flood the target with fake replies and hit the "right" reply
before real reply comes. So random qIDs don't add much protection anyway,
even if this feature is implemented in udns, and using all available
techniques wont solve it either.
See also long comment in udns_resolver.c, udns_newid().
Assumptions about RRs returned
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Currently udns processes records in the reply it received sequentially.
This means that order of the records is significant. For example, if
we asked for foo.bar A, but the server returned that foo.bar is a CNAME
(alias) for bar.baz, and bar.baz, in turn, has address 1.2.3.4, when
the CNAME should come first in reply, followed by A. While DNS specs
does not say anything about order of records - it's an rrSET - unordered, -
I think an implementation which returns the records in "wrong" order is
somewhat insane...
CNAME recursion
~~~~~~~~~~~~~~~
Another interesting point is the handling of CNAMEs returned as replies
to non-CNAME queries. If we asked for foo.bar A, but it's a CNAME, udns
expects BOTH the CNAME itself and the target DN to be present in the reply.
In other words, udns DOES NOT RECURSE CNAMES. If we asked for foo.bar A,
but only record in reply was that foo.bar is a CNAME for bar.baz, udns will
return no records to an application (NXDOMAIN). Strictly speaking, udns
should repeat the query asking for bar.baz A, and recurse. But since it's
stub resolver, recursive resolver should recurse for us instead.
It's not very difficult to implement, however. Probably with some (global?)
flag to en/dis-able the feature. Provided there's some demand for it.
To clarify: udns handles CNAME recursion in a single reply packet just fine.
Note also that standard gethostbyname() routine does not recurse in this
situation, too.
Error reporting
~~~~~~~~~~~~~~~
Too many places in the code (various failure paths) sets generic "TEMPFAIL"
error condition. For example, if no nameserver replied to our query, an
application will get generic TEMPFAIL, instead of something like TIMEDOUT.
This probably should be fixed, but most applications don't care about the
exact reasons of failure - 4 common cases are already too much:
- query returned some valid data
- NXDOMAIN
- valid domain but no data of requested type - =NXDOMAIN in most cases
- temporary error - this one sometimes (incorrectly!) treated as NXDOMAIN
by (naive) applications.
DNS isn't yes/no, it's at least 3 variants, temp err being the 3rd important
case! And adding more variations for the temp error case is complicating things
even more - again, from an application writer standpoint. For diagnostics,
such more specific error cases are of good help.
Planned API changes
~~~~~~~~~~~~~~~~~~~
At least one thing I want to change for some future version is a way how
queries are submitted and how replies are handled.
I want to made dns_query object to be owned by an application. So that instead
of udns library allocating it for the lifetime of query, it will be pre-
allocated by an application. This simplifies and enhances query submitting
interface, and complicates it a bit too, in simplest cases.
Currently, we have:
dns_submit_dn(dn, cls, typ, flags, parse, cbck, data)
dns_submit_p(name, cls, typ, flags, parse, cbck, data)
dns_submit_a4(ctx, name, flags, cbck, data)
and so on -- with many parameters missed for type-specific cases, but generic
cases being too complex for most common usage.
Instead, with dns_query being owned by an app, we will be able to separately
set up various parts of the query - domain name (various forms), type&class,
parser, flags, callback... and even change them at runtime. And we will also
be able to reuse query structures, instead of allocating/freeing them every
time. So the whole thing will look something like:
q = dns_alloc_query();
dns_submit(dns_q_flags(dns_q_a4(q, name, cbck), DNS_F_NOSRCH), data);
The idea is to have a set of functions accepting struct dns_query* and
returning it (so the calls can be "nested" like the above), to set up
relevant parts of the query - specific type of callback, conversion from
(type-specific) query parameters into a domain name (this is for type-
specific query initializers), and setting various flags and options and
type&class things.
One example where this is almost essential - if we want to support
per-query set of nameservers (which isn't at all useless: imagine a
high-volume mail server, were we want to direct DNSBL queries to a separate
set of nameservers, and rDNS queries to their own set and so on). Adding
another argument (set of nameservers to use) to EVERY query submitting
routine is.. insane. Especially since in 99% cases it will be set to
default NULL. But with such "nesting" of query initializers, it becomes
trivial.
This change (the way how queries gets submitted) will NOT break API/ABI
compatibility with old versions, since the new submitting API works in
parallel with current (and current will use the new one as building
blocks, instead of doing all work at once).
Another way to do the same is to manipulate query object right after a
query has been submitted, but before any events processing (during this
time, query object is allocated and initialized, but no actual network
packets were sent - it will happen on the next event processing). But
this way it become impossible to perform syncronous resolver calls, since
those calls hide query objects they use internally.
Speaking of replies handling - the planned change is to stop using dynamic
memory (malloc) inside the library. That is, instead of allocating a buffer
for a reply dynamically in a parsing routine (or memdup'ing the raw reply
packet if no parsing routine is specified), I want udns to return the packet
buffer it uses internally, and change parsing routines to expect a buffer
for result. When parsing, a routine will return true amount of memory it
will need to place the result, regardless of whenever it has enough room
or not, so that an application can (re)allocate properly sized buffer and
call a parsing routine again.
This, in theory, also can be done without breaking current API/ABI, but in
that case we'll again need a parallel set of routines (parsing included),
which makes the library more complicated with too many ways of doing the
same thing. Still, code reuse is at good level.
Another modification I plan to include is to have an ability to work in
terms of domain names (DNs) as used with on-wire DNS packets, not only
with asciiz representations of them. For this to work, the above two
changes (query submission and result passing) have to be completed first
(esp. the query submission part), so that it will be possible to specify
some additional query flags (for example) to request domain names instead
of the text strings, and to allow easy query submissions with either DNs
or text strings.

View File

@ -0,0 +1,59 @@
TODO
The following is mostly an internal, not user-visible stuff.
* rearrange an API to make dns_query object owned by application,
so that it'll look like this:
struct dns_query *q;
q = dns_query_alloc(ctx);
dns_query_set(q, options, domain_name, flags, ...);
dns_query_submit(ctx, q);
For more information see NOTES file, section "Planned API changes".
* allow NULL callbacks? Or provide separate resolver
context list of queries which are done but wich did not
have callback, and dns_pick() routine to retrieve results
from this query, i.e. allow non-callback usage? The
non-callback usage may be handy sometimes (any *good*
example?), but it will be difficult to provide type-safe
non-callback interface due to various RR-specific types
in use.
* DNS_OPT_FLAGS should be DNS_OPT_ADDFLAGS and DNS_OPT_SETFLAGS.
Currently one can't add a single flag bit but preserve
existing bits... at least not without retrieving all current
flags before, which isn't that bad anyway.
* dns_set_opts() may process flags too (such as aaonly etc)
* a way to disable $NSCACHEIP et al processing?
(with now separate dns_init() and dns_reset(), it has finer
control, but still no way to init from system files but ignore
environment variables and the like)
* initialize/open the context automatically, and be more
liberal about initialization in general?
* dns_init(ctx, do_open) - make the parameter opposite, aka
dns_init(ctx, skip_open) ?
* allow TCP queue?
* more accurate error reporting. Currently, udns always returns TEMPFAIL,
but don't specify why it happened (ENOMEM, timeout, etc).
* check the error value returned by recvfrom() and
sendto() and determine which errors to ignore.
* maybe merge dns_timeouts() and dns_ioevent(), to have
only one entry point for everything? For traditional
select-loop-based eventloop it may be easier, but for
callback-driven event loops the two should be separate.
Provide an option, or a single dns_events() entry point
for select-loop approach, or just call dns_ioevent()
from within dns_timeouts() (probably after renaming
it to be dns_events()) ?
* implement /etc/hosts lookup too, ala [c-]ares??
* sortlist support?

View File

@ -0,0 +1,268 @@
# configure.lib
# a library of shell routines for simple autoconf system
#
set -e
ac_substitutes=
rm -f conftest* config.log
exec 5>config.log
cat <<EOF >&5
This file contains any messages produced by compilers etc while
running configure, to aid debugging if configure script makes a mistake.
EOF
case `echo "a\c"` in
*c*) ac_en=-n ac_ec= ;;
*) ac_en= ac_ec='\c' ;;
esac
##### Messages
ac_msg() {
echo $ac_en "$*... $ac_ec"
echo ">>> $*" >&5
}
ac_checking() {
echo $ac_en "checking $*... $ac_ec"
echo ">>> checking $*" >&5
}
ac_result() {
echo "$1"
echo "=== $1" >&5
}
ac_fatal() {
echo "configure: fatal: $*" >&2
echo "=== FATAL: $*" >&5
exit 1
}
ac_warning() {
echo "configure: warning: $*" >&2
echo "=== WARNING: $*" >&5
}
ac_ign() {
"$@" || :
}
# ac_run command...
# captures output in conftest.out
ac_run() {
# apparently UnixWare (for one) /bin/sh optimizes the following "if"
# "away", by checking if there's such a command BEFORE redirecting
# output. So error message (like "gcc: command not found") goes
# to stderr instead of to conftest.out, and `cat conftest.out' below
# fails.
if "$@" >conftest.out 2>&1; then
return 0
else
echo "==== Command invocation failed. Command line was:" >&5
echo "$*" >&5
echo "==== compiler input was:" >&5
cat conftest.c >&5
echo "==== output was:" >&5
cat conftest.out >&5
echo "====" >&5
return 1
fi
}
# common case for ac_verbose: yes/no result
ac_yesno() {
ac_checking "$1"
shift
if "$@"; then
ac_result yes
return 0
else
ac_result no
return 1
fi
}
ac_subst() {
ac_substitutes="$ac_substitutes $*"
}
ac_define() {
CDEFS="$CDEFS -D$1=${2:-1}"
}
ac_have() {
ac_what=$1; shift
if "$@"; then
ac_define HAVE_$ac_what
eval ac_have_$ac_what=yes
return 0
else
eval ac_have_$ac_what=no
return 1
fi
}
##### Compiling, linking
# run a compiler
ac_run_compiler() {
rm -f conftest*; cat >conftest.c
ac_run $CC $CFLAGS $CDEFS "$@" conftest.c
}
ac_compile() {
ac_run_compiler -c
}
ac_link() {
ac_run_compiler -o conftest $LIBS "$@"
}
ac_cpp() {
ac_run_compiler -E "$@"
}
### check for C compiler. Set $CC, $CFLAGS etc
ac_prog_c_compiler_v() {
ac_checking "for C compiler"
rm -f conftest*
echo 'int main(int argc, char **argv) { return 0; }' >conftest.c
if [ -n "$CC" ]; then
if ac_run $CC -o conftest conftest.c && ac_run ./conftest; then
ac_result "\$CC ($CC)"
else
ac_result no
ac_fatal "\$CC ($CC) is not a working compiler"
fi
else
for cc in gcc cc ; do
if ac_run $cc -o conftest conftest.c && ac_run ./conftest; then
ac_result "$cc"
CC=$cc
break
fi
done
if [ -z "$CC" ]; then
ac_result no
ac_fatal "no working C compiler found in \$PATH. please set \$CC variable"
fi
fi
if [ -z "$CFLAGS" ]; then
if ac_yesno "whenever C compiler ($CC) is GNU CC" \
ac_grep_cpp yEs_mAsTeR <<EOF
#ifdef __GNUC__
yEs_mAsTeR;
#endif
EOF
then
CFLAGS="-Wall -W -O2 -pipe"
else
CFLAGS=-O
fi
fi
cc="$CC $CFLAGS"
ccld="$cc"
if [ -n "$LDFLAGS" ]; then ccld="$ccld $LDFLAGS"; fi
if [ -n "$LIBS" ]; then ccld="$ccld $LIBS"; fi
if ac_yesno "whenever the C compiler ($ccld)
can produce executables" \
ac_compile_run <<EOF
int main() { return 0; }
EOF
then :
else
ac_fatal "no working C compiler found"
fi
LD='$(CC)'
[ -n "$AR" ] || AR=ar
[ -n "$ARFLAGS" ] || ARFLAGS=rv
[ -n "$AWK" ] || AWK=awk
ac_substitutes="$ac_substitutes CC CFLAGS CDEFS LD LDFLAGS LIBS AR ARFLAGS AWK"
}
ac_prog_ranlib_v() {
ac_checking "for ranlib"
if [ -n "$RANLIB" ]; then
ac_result "\$RANLIB ($RANLIB)"
else
ifs="$IFS"
IFS=:
for dir in $PATH; do
[ -n "$dir" ] || dir=.
if [ -f $dir/ranlib ]; then
RANLIB=ranlib
break
fi
done
IFS="$ifs"
if [ -z "$RANLIB" ]; then ac_result no; RANLIB=:
else ac_result "$RANLIB"
fi
fi
ac_substitutes="$ac_substitutes RANLIB"
}
ac_library_find_v() {
ac_checking "for libraries needed for $1"
shift
fond=
rm -f conftest*; cat >conftest.c
for lib in "$@"; do
if ac_run $CC $CFLAGS $LDFLAGS conftest.c -o conftest $LIBS $lib; then
found=y
break
fi
done
if [ ! "$found" ]; then
ac_result "not found"
return 1
fi
if [ -z "$lib" ]; then
ac_result "ok (none needed)"
else
ac_result "ok ($lib)"
LIBS="$LIBS $lib"
fi
}
ac_compile_run() {
ac_link "$@" && ac_run ./conftest
}
ac_grep_cpp() {
pattern="$1"; shift
ac_cpp "$@" && grep "$pattern" conftest.out >/dev/null
}
ac_output() {
for var in $ac_substitutes; do
eval echo "\"s|@$var@|\$$var|\""
done >conftest.sed
for file in "$@"; do
ac_msg "creating $file"
if [ -f $file.in ]; then
sed -f conftest.sed $file.in > $file.tmp
mv -f $file.tmp $file
ac_result ok
else
ac_result failed
ac_fatal "$file.in not found"
fi
done
rm -f conftest*
}
ac_config_h() {
h=${1:-config.h}
ac_msg "creating $h"
rm -f $1.tmp
echo "/* automatically generated by configure. */" > $h.tmp
echo "$CDEFS" | tr ' ' '
' | sed -e 's/^-D/#define /' -e 's/=/ /' >> $h.tmp
if [ -f $h ] && cmp -s $h.tmp $h ; then
rm -f $h.tmp
ac_result unchanged
else
mv -f $h.tmp $h
ac_result ok
fi
CDEFS=-DHAVE_CONFIG_H
}

View File

@ -0,0 +1,195 @@
.\" dnsget.1: dnsget manpage
.\"
.\" Copyright (C) 2005-2014 Michael Tokarev <mjt+udns@tls.msk.ru>
.\" This file is part of UDNS library, an async DNS stub resolver.
.\"
.\" This library is free software; you can redistribute it and/or
.\" modify it under the terms of the GNU Lesser General Public
.\" License as published by the Free Software Foundation; either
.\" version 2.1 of the License, or (at your option) any later version.
.\"
.\" This library is distributed in the hope that it will be useful,
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
.\" Lesser General Public License for more details.
.\"
.\" You should have received a copy of the GNU Lesser General Public
.\" License along with this library, in file named COPYING.LGPL; if not,
.\" write to the Free Software Foundation, Inc., 59 Temple Place,
.\" Suite 330, Boston, MA 02111-1307 USA
.TH dnsget 1 "Jan 2014" "User Utilities"
.SH NAME
dnsget \- DNS lookup utility
.SH SYNOPSYS
.B dnsget
.RB [\| \-v \||\| \-q \|]
.RB [\| \-c
.IR class \|]
.RB [\| \-t
.IR type \|]
.RB [\| \-o
.IR opt , opt ,...]
.IR name \|.\|.\|.
.SH DESCRIPTION
.B dnsget
is a simple command-line to perform DNS lookups, similar to
.BR host (1)
and
.BR dig (1).
It is useable for both interactive/debugging scenarious and
in scripts.
The program is implemented using
.BR udns (3)
library.
.PP
By default,
.B dnsget
produces a human-readable output, similar to
.RS
.nf
alias.example.com. CNAME www.example.com.
www.example.com. A 192.168.1.1
www.example.com. MX 10 mx.example.com.
.fi
.RE
which is just sufficient to see how a given name resolves.
Output format is controllable with
.B \-v
and
.B \-q
options -- the former increases verbosity level up to printing
the whole DNS contents of all packets sent and received, which
is suitable for debugging DNS problems, while the latter reduces
the level, making output more quiet, up to bare result with no
error messages, which is good for scripts.
.SH OPTIONS
The following options are recognized by
.BR dnsget :
.TP
.B \-v
produce more detailed output. More
.BR \-v 's
means more details will be produced. With single
.BR \-v , dnsget
will print contents of all received DNS packets (in a readable format),
while with
.BR \-vv ,
it will output all outgoing DNS packets too.
.TP
.B \-q
the opposite for \fB\-v\fR -- produce less detailed output.
With single
.BR \-q , dnsget
will only show (decoded) data from final DNS resource records (RR),
while
.B \-qq
also suppresses error messages.
.TP
\fB\-t \fItype\fR
request record(s) of the given type \fItype\fR. By default,
.B dnsget
will ask for IPv4 address (A) record, or for PTR record if the
argument in question is an IPv4 or IPv6 address. Recognized
types include A, AAAA, MX, TXT, CNAME, PTR, NS, SOA, ANY and
others.
.TP
\fB\-c \fIclass\fR
request DNS record(s) of the given class \fIclass\fR. By
default
.B dnsget
uses IN class. Valid classes include IN, CH, HS, ANY.
.TP
.B \-a
(compatibility option). Equivalent to setting query type to
.B ANY
and increasing verbosity level
.RB ( \-v ).
.TP
.B \-C
(planned)
.TP
.B \-x
(planned)
.TP
\fB\-o \fIopt\fR,\fIopt\fR,...
(may be specified several times).
Set resolver options (in a form \fIoption\fR:\fIvalue\fR) as if they
were set in
.RB $ RES_OPTIONS
environment variable, or set query flags:
.RS
.TP
\fBtimeout\fR:\fIsec\fR
Set initial query timeout to \fIsec\fR.
.TP
\fBattempts\fR:\fInum\fR
(re)try every query \fInum\fR times before failing.
.TP
\fBudpbuf\fR:\fIbytes\fR
set DNS UDP buffer size to \fIbytes\fR bytes. Valid values
are from 512 to 65535. If \fIbytes\fR is greather than 512,
EDNS0 (RFC 2671) extensions will be used.
.TP
\fBport\fR:\fInum\fR
Use given UDP port number \fInum\fR instead of the default port 53 (domain).
.TP
\fBaa\fR
set AA (auth only) query bit.
.TP
\fBnord\fR
do not set RD (recursion desired) query bit (set by default).
.TP
\fBdnssec\fR or \fBdo\fR
set DNSSEC OK (DO) query flag (\fBdnsget\fR does not verify DNSSEC signatures,
only displays them; this is set in EDNS RR).
.TP
\fBcd\fR
set CD (checking disabled) query bit.
.RE
.TP
\fB\-n \fInameserver\fR
Use the given nameserver(s) (may be specified more than once)
instead of the default. Using this option has the same same effect as
.RB $ NSCACHEIP
or
.RB $ NAMESERVERS
environment variables, with the only difference that only IPv4 addresses
are recognized for now, and it is possible to specify names (which will
be resolved using default settings) instead of IP addresses.
.TP
.B \-h
print short help and exit.
.SH "RETURN VALUE"
When all names where resovled successefully,
.B dnsget
exits with zero exit status. If at least one name was not found,
.B dnsget
will exit with return code 100. If some other error occured during
name resolution, it will exit with code 99. In case of usage or
initialization error,
.B dnsget
will return 1.
.SH "SEE ALSO"
.BR host (1)
.BR dig (1)
.BR resolv.conf (5)
.BR udns (3).

View File

@ -0,0 +1,759 @@
/* dnsget.c
simple host/dig-like application using UDNS library
Copyright (C) 2005 Michael Tokarev <mjt@corpit.ru>
This file is part of UDNS library, an async DNS stub resolver.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library, in file named COPYING.LGPL; if not,
write to the Free Software Foundation, Inc., 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef WINDOWS
#include <windows.h>
#include <winsock2.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <unistd.h>
#endif
#include <time.h>
#include <stdarg.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "udns.h"
#ifndef HAVE_GETOPT
# include "getopt.c"
#endif
#ifndef AF_INET6
# define AF_INET6 10
#endif
static char *progname;
static int verbose = 1;
static int errors;
static int notfound;
/* verbosity level:
* <0 - bare result
* 0 - bare result and error messages
* 1 - readable result
* 2 - received packet contents and `trying ...' stuff
* 3 - sent and received packet contents
*/
static void die(int errnum, const char *fmt, ...) {
va_list ap;
fprintf(stderr, "%s: ", progname);
va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap);
if (errnum) fprintf(stderr, ": %s\n", strerror(errnum));
else putc('\n', stderr);
fflush(stderr);
exit(1);
}
static const char *dns_xntop(int af, const void *src) {
static char buf[6*5+4*4];
return dns_ntop(af, src, buf, sizeof(buf));
}
struct query {
const char *name; /* original query string */
unsigned char *dn; /* the DN being looked up */
enum dns_type qtyp; /* type of the query */
};
static void query_free(struct query *q) {
free(q->dn);
free(q);
}
static struct query *
query_new(const char *name, const unsigned char *dn, enum dns_type qtyp) {
struct query *q = malloc(sizeof(*q));
unsigned l = dns_dnlen(dn);
unsigned char *cdn = malloc(l);
if (!q || !cdn) die(0, "out of memory");
memcpy(cdn, dn, l);
q->name = name;
q->dn = cdn;
q->qtyp = qtyp;
return q;
}
static enum dns_class qcls = DNS_C_IN;
static void
dnserror(struct query *q, int errnum) {
if (verbose >= 0)
fprintf(stderr, "%s: unable to lookup %s record for %s: %s\n", progname,
dns_typename(q->qtyp), dns_dntosp(q->dn), dns_strerror(errnum));
if (errnum == DNS_E_NXDOMAIN || errnum == DNS_E_NODATA)
++notfound;
else
++errors;
query_free(q);
}
static const unsigned char *
printtxt(const unsigned char *c) {
unsigned n = *c++;
const unsigned char *e = c + n;
if (verbose > 0) while(c < e) {
if (*c < ' ' || *c >= 127) printf("\\%03u", *c);
else if (*c == '\\' || *c == '"') printf("\\%c", *c);
else putchar(*c);
++c;
}
else
fwrite(c, n, 1, stdout);
return e;
}
static void
printhex(const unsigned char *c, const unsigned char *e) {
while(c < e)
printf("%02x", *c++);
}
static unsigned char to_b64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static void
printb64(const unsigned char *c, const unsigned char *e) {
while(c < e) {
putchar(to_b64[c[0] >> 2]);
if (c+1 < e) {
putchar(to_b64[(c[0] & 0x3) << 4 | c[1] >> 4]);
if (c+2 < e) {
putchar(to_b64[(c[1] & 0xf) << 2 | c[2] >> 6]);
putchar(to_b64[c[2] & 0x3f]);
}
else {
putchar(to_b64[(c[1] & 0xf) << 2]);
putchar('=');
break;
}
}
else {
putchar(to_b64[(c[0] & 0x3) << 4]);
putchar('=');
putchar('=');
break;
}
c += 3;
}
}
static void
printdate(time_t time) {
struct tm *tm = gmtime(&time);
printf("%04d%02d%02d%02d%02d%02d",
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
}
static void
printrr(const struct dns_parse *p, struct dns_rr *rr) {
const unsigned char *pkt = p->dnsp_pkt;
const unsigned char *end = p->dnsp_end;
const unsigned char *dptr = rr->dnsrr_dptr;
const unsigned char *dend = rr->dnsrr_dend;
unsigned char *dn = rr->dnsrr_dn;
const unsigned char *c;
unsigned n;
if (verbose > 0) {
if (verbose > 1) {
if (!p->dnsp_rrl && !rr->dnsrr_dn[0] && rr->dnsrr_typ == DNS_T_OPT) {
printf(";EDNS%d OPT record (UDPsize: %d, ERcode: %d, Flags: 0x%02x): %d bytes\n",
(rr->dnsrr_ttl>>16) & 0xff, /* version */
rr->dnsrr_cls, /* udp size */
(rr->dnsrr_ttl>>24) & 0xff, /* extended rcode */
rr->dnsrr_ttl & 0xffff, /* flags */
rr->dnsrr_dsz);
return;
}
n = printf("%s.", dns_dntosp(rr->dnsrr_dn));
printf("%s%u\t%s\t%s\t",
n > 15 ? "\t" : n > 7 ? "\t\t" : "\t\t\t",
rr->dnsrr_ttl,
dns_classname(rr->dnsrr_cls),
dns_typename(rr->dnsrr_typ));
}
else
printf("%s. %s ", dns_dntosp(rr->dnsrr_dn), dns_typename(rr->dnsrr_typ));
}
switch(rr->dnsrr_typ) {
case DNS_T_CNAME:
case DNS_T_PTR:
case DNS_T_NS:
case DNS_T_MB:
case DNS_T_MD:
case DNS_T_MF:
case DNS_T_MG:
case DNS_T_MR:
if (dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN) <= 0) goto xperr;
printf("%s.", dns_dntosp(dn));
break;
case DNS_T_A:
if (rr->dnsrr_dsz != 4) goto xperr;
printf("%d.%d.%d.%d", dptr[0], dptr[1], dptr[2], dptr[3]);
break;
case DNS_T_AAAA:
if (rr->dnsrr_dsz != 16) goto xperr;
printf("%s", dns_xntop(AF_INET6, dptr));
break;
case DNS_T_MX:
c = dptr + 2;
if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || c != dend) goto xperr;
printf("%d %s.", dns_get16(dptr), dns_dntosp(dn));
break;
case DNS_T_TXT:
/* first verify it */
for(c = dptr; c < dend; c += n) {
n = *c++;
if (c + n > dend) goto xperr;
}
c = dptr; n = 0;
while (c < dend) {
if (verbose > 0) printf(n++ ? "\" \"":"\"");
c = printtxt(c);
}
if (verbose > 0) putchar('"');
break;
case DNS_T_HINFO: /* CPU, OS */
c = dptr;
n = *c++; if ((c += n) >= dend) goto xperr;
n = *c++; if ((c += n) != dend) goto xperr;
c = dptr;
if (verbose > 0) putchar('"');
c = printtxt(c);
if (verbose > 0) printf("\" \""); else putchar(' ');
printtxt(c);
if (verbose > 0) putchar('"');
break;
case DNS_T_WKS:
c = dptr;
if (dptr + 4 + 2 >= end) goto xperr;
printf("%s %d", dns_xntop(AF_INET, dptr), dptr[4]);
c = dptr + 5;
for (n = 0; c < dend; ++c, n += 8) {
if (*c) {
unsigned b;
for (b = 0; b < 8; ++b)
if (*c & (1 << (7-b))) printf(" %d", n + b);
}
}
break;
case DNS_T_SRV: /* prio weight port targetDN */
c = dptr;
c += 2 + 2 + 2;
if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || c != dend) goto xperr;
c = dptr;
printf("%d %d %d %s.",
dns_get16(c+0), dns_get16(c+2), dns_get16(c+4),
dns_dntosp(dn));
break;
case DNS_T_NAPTR: /* order pref flags serv regexp repl */
c = dptr;
c += 4; /* order, pref */
for (n = 0; n < 3; ++n)
if (c >= dend) goto xperr;
else c += *c + 1;
if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || c != dend) goto xperr;
c = dptr;
printf("%u %u", dns_get16(c+0), dns_get16(c+2));
c += 4;
for(n = 0; n < 3; ++n) {
putchar(' ');
if (verbose > 0) putchar('"');
c = printtxt(c);
if (verbose > 0) putchar('"');
}
printf(" %s.", dns_dntosp(dn));
break;
case DNS_T_KEY:
case DNS_T_DNSKEY:
/* flags(2) proto(1) algo(1) pubkey */
case DNS_T_DS:
case DNS_T_DLV:
/* ktag(2) proto(1) algo(1) pubkey */
c = dptr;
if (c + 2 + 1 + 1 > dend) goto xperr;
printf("%d %d %d", dns_get16(c), c[2], c[3]);
c += 2 + 1 + 1;
if (c < dend) {
putchar(' ');
printb64(c, dend);
}
break;
case DNS_T_SIG:
case DNS_T_RRSIG:
/* type(2) algo(1) labels(1) ottl(4) sexp(4) sinc(4) tag(2) sdn sig */
c = dptr;
c += 2 + 1 + 1 + 4 + 4 + 4 + 2;
if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0) goto xperr;
printf("%s %u %u %u ",
dns_typename(dns_get16(dptr)), dptr[2], dptr[3], dns_get32(dptr+4));
printdate(dns_get32(dptr+8));
putchar(' ');
printdate(dns_get32(dptr+12));
printf(" %d %s. ", dns_get16(dptr+10), dns_dntosp(dn));
printb64(c, dend);
break;
case DNS_T_SSHFP: /* algo(1), fp type(1), fp... */
if (dend < dptr + 3) goto xperr;
printf("%u %u ", dptr[0], dptr[1]); /* algo, fp type */
printhex(dptr + 2, dend);
break;
#if 0 /* unused RR types? */
case DNS_T_NSEC: /* nextDN bitmaps */
c = dptr;
if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0) goto xperr;
printf("%s.", dns_dntosp(dn));
unfinished.
break;
#endif
case DNS_T_SOA:
c = dptr;
if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 ||
dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 ||
c + 4*5 != dend)
goto xperr;
dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN);
printf("%s. ", dns_dntosp(dn));
dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN);
printf("%s. ", dns_dntosp(dn));
printf("%u %u %u %u %u",
dns_get32(dptr), dns_get32(dptr+4), dns_get32(dptr+8),
dns_get32(dptr+12), dns_get32(dptr+16));
break;
case DNS_T_MINFO:
c = dptr;
if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 ||
dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 ||
c != dend)
goto xperr;
dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN);
printf("%s. ", dns_dntosp(dn));
dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN);
printf("%s.", dns_dntosp(dn));
break;
case DNS_T_NULL:
default:
printhex(dptr, dend);
break;
}
putchar('\n');
return;
xperr:
printf("<parse error>\n");
++errors;
}
static int
printsection(struct dns_parse *p, int nrr, const char *sname) {
struct dns_rr rr;
int r;
if (!nrr) return 0;
if (verbose > 1) printf("\n;; %s section (%d):\n", sname, nrr);
p->dnsp_rrl = nrr;
while((r = dns_nextrr(p, &rr)) > 0)
printrr(p, &rr);
if (r < 0) printf("<<ERROR>>\n");
return r;
}
/* dbgcb will only be called if verbose > 1 */
static void
dbgcb(int code, const struct sockaddr *sa, unsigned slen,
const unsigned char *pkt, int r,
const struct dns_query *unused_q, void *unused_data) {
struct dns_parse p;
const unsigned char *cur, *end;
int numqd;
if (code > 0) {
printf(";; trying %s.\n", dns_dntosp(dns_payload(pkt)));
printf(";; sending %d bytes query to ", r);
}
else
printf(";; received %d bytes response from ", r);
if (sa->sa_family == AF_INET && slen >= sizeof(struct sockaddr_in))
printf("%s port %d\n",
dns_xntop(AF_INET, &((struct sockaddr_in*)sa)->sin_addr),
htons(((struct sockaddr_in*)sa)->sin_port));
#ifdef HAVE_IPv6
else if (sa->sa_family == AF_INET6 && slen >= sizeof(struct sockaddr_in6))
printf("%s port %d\n",
dns_xntop(AF_INET6, &((struct sockaddr_in6*)sa)->sin6_addr),
htons(((struct sockaddr_in6*)sa)->sin6_port));
#endif
else
printf("<<unknown socket type %d>>\n", sa->sa_family);
if (code > 0 && verbose < 3) {
putchar('\n');
return;
}
if (code == -2) printf(";; reply from unexpected source\n");
if (code == -5) printf(";; reply to a query we didn't sent (or old)\n");
if (r < DNS_HSIZE) {
printf(";; short packet (%d bytes)\n", r);
return;
}
if (dns_opcode(pkt) != 0)
printf(";; unexpected opcode %d\n", dns_opcode(pkt));
if (dns_tc(pkt) != 0)
printf(";; warning: TC bit set, probably incomplete reply\n");
printf(";; ->>HEADER<<- opcode: ");
switch(dns_opcode(pkt)) {
case 0: printf("QUERY"); break;
case 1: printf("IQUERY"); break;
case 2: printf("STATUS"); break;
default: printf("UNKNOWN(%u)", dns_opcode(pkt)); break;
}
printf(", status: %s, id: %d, size: %d\n;; flags:",
dns_rcodename(dns_rcode(pkt)), dns_qid(pkt), r);
if (dns_qr(pkt)) printf(" qr");
if (dns_aa(pkt)) printf(" aa");
if (dns_tc(pkt)) printf(" tc");
if (dns_rd(pkt)) printf(" rd");
if (dns_ra(pkt)) printf(" ra");
/* if (dns_z(pkt)) printf(" z"); only one reserved bit left */
if (dns_ad(pkt)) printf(" ad");
if (dns_cd(pkt)) printf(" cd");
numqd = dns_numqd(pkt);
printf("; QUERY: %d, ANSWER: %d, AUTHORITY: %d, ADDITIONAL: %d\n",
numqd, dns_numan(pkt), dns_numns(pkt), dns_numar(pkt));
if (numqd != 1)
printf(";; unexpected number of entries in QUERY section: %d\n",
numqd);
printf("\n;; QUERY SECTION (%d):\n", numqd);
cur = dns_payload(pkt);
end = pkt + r;
while(numqd--) {
if (dns_getdn(pkt, &cur, end, p.dnsp_dnbuf, DNS_MAXDN) <= 0 ||
cur + 4 > end) {
printf("; invalid query section\n");
return;
}
r = printf(";%s.", dns_dntosp(p.dnsp_dnbuf));
printf("%s%s\t%s\n",
r > 23 ? "\t" : r > 15 ? "\t\t" : r > 7 ? "\t\t\t" : "\t\t\t\t",
dns_classname(dns_get16(cur+2)), dns_typename(dns_get16(cur)));
cur += 4;
}
p.dnsp_pkt = pkt;
p.dnsp_cur = p.dnsp_ans = cur;
p.dnsp_end = end;
p.dnsp_qdn = NULL;
p.dnsp_qcls = p.dnsp_qtyp = 0;
p.dnsp_ttl = 0xffffffffu;
p.dnsp_nrr = 0;
r = printsection(&p, dns_numan(pkt), "ANSWER");
if (r == 0)
r = printsection(&p, dns_numns(pkt), "AUTHORITY");
if (r == 0)
r = printsection(&p, dns_numar(pkt), "ADDITIONAL");
putchar('\n');
}
static void dnscb(struct dns_ctx *ctx, void *result, void *data) {
int r = dns_status(ctx);
struct query *q = data;
struct dns_parse p;
struct dns_rr rr;
unsigned nrr;
unsigned char dn[DNS_MAXDN];
const unsigned char *pkt, *cur, *end;
if (!result) {
dnserror(q, r);
return;
}
pkt = result; end = pkt + r; cur = dns_payload(pkt);
dns_getdn(pkt, &cur, end, dn, sizeof(dn));
dns_initparse(&p, NULL, pkt, cur, end);
p.dnsp_qcls = p.dnsp_qtyp = 0;
nrr = 0;
while((r = dns_nextrr(&p, &rr)) > 0) {
if (!dns_dnequal(dn, rr.dnsrr_dn)) continue;
if ((qcls == DNS_C_ANY || qcls == rr.dnsrr_cls) &&
(q->qtyp == DNS_T_ANY || q->qtyp == rr.dnsrr_typ))
++nrr;
else if (rr.dnsrr_typ == DNS_T_CNAME && !nrr) {
if (dns_getdn(pkt, &rr.dnsrr_dptr, end,
p.dnsp_dnbuf, sizeof(p.dnsp_dnbuf)) <= 0 ||
rr.dnsrr_dptr != rr.dnsrr_dend) {
r = DNS_E_PROTOCOL;
break;
}
else {
if (verbose == 1) {
printf("%s.", dns_dntosp(dn));
printf(" CNAME %s.\n", dns_dntosp(p.dnsp_dnbuf));
}
dns_dntodn(p.dnsp_dnbuf, dn, sizeof(dn));
}
}
}
if (!r && !nrr)
r = DNS_E_NODATA;
if (r < 0) {
dnserror(q, r);
free(result);
return;
}
if (verbose < 2) { /* else it is already printed by dbgfn */
dns_rewind(&p, NULL);
p.dnsp_qtyp = q->qtyp == DNS_T_ANY ? 0 : q->qtyp;
p.dnsp_qcls = qcls == DNS_C_ANY ? 0 : qcls;
while(dns_nextrr(&p, &rr))
printrr(&p, &rr);
}
free(result);
query_free(q);
}
int main(int argc, char **argv) {
int i;
int fd;
fd_set fds;
struct timeval tv;
time_t now;
char *ns[DNS_MAXSERV];
int nns = 0;
struct query *q;
enum dns_type qtyp = 0;
struct dns_ctx *nctx = NULL;
int flags = 0;
if (!(progname = strrchr(argv[0], '/'))) progname = argv[0];
else argv[0] = ++progname;
if (argc <= 1)
die(0, "try `%s -h' for help", progname);
if (dns_init(NULL, 0) < 0 || !(nctx = dns_new(NULL)))
die(errno, "unable to initialize dns library");
/* we keep two dns contexts: one may be needed to resolve
* nameservers if given as names, using default options.
*/
while((i = getopt(argc, argv, "vqt:c:an:o:f:h")) != EOF) switch(i) {
case 'v': ++verbose; break;
case 'q': --verbose; break;
case 't':
if (optarg[0] == '*' && !optarg[1])
i = DNS_T_ANY;
else if ((i = dns_findtypename(optarg)) <= 0)
die(0, "unrecognized query type `%s'", optarg);
qtyp = i;
break;
case 'c':
if (optarg[0] == '*' && !optarg[1])
i = DNS_C_ANY;
else if ((i = dns_findclassname(optarg)) < 0)
die(0, "unrecognized query class `%s'", optarg);
qcls = i;
break;
case 'a':
qtyp = DNS_T_ANY;
++verbose;
break;
case 'n':
if (nns >= DNS_MAXSERV)
die(0, "too many nameservers, %d max", DNS_MAXSERV);
ns[nns++] = optarg;
break;
case 'o':
case 'f': {
char *opt;
const char *const delim = " \t,;";
for(opt = strtok(optarg, delim); opt != NULL; opt = strtok(NULL, delim)) {
if (dns_set_opts(NULL, optarg) == 0)
;
else if (strcmp(opt, "aa") == 0) flags |= DNS_AAONLY;
else if (strcmp(optarg, "nord") == 0) flags |= DNS_NORD;
else if (strcmp(optarg, "dnssec") == 0) flags |= DNS_SET_DO;
else if (strcmp(optarg, "do") == 0) flags |= DNS_SET_DO;
else if (strcmp(optarg, "cd") == 0) flags |= DNS_SET_CD;
else
die(0, "invalid option: `%s'", opt);
}
break;
}
case 'h':
printf(
"%s: simple DNS query tool (using udns version %s)\n"
"Usage: %s [options] domain-name...\n"
"where options are:\n"
" -h - print this help and exit\n"
" -v - be more verbose\n"
" -q - be less verbose\n"
" -t type - set query type (A, AAA, PTR etc)\n"
" -c class - set query class (IN (default), CH, HS, *)\n"
" -a - equivalent to -t ANY -v\n"
" -n ns - use given nameserver(s) instead of default\n"
" (may be specified multiple times)\n"
" -o opt,opt,... (comma- or space-separated list,\n"
" may be specified more than once):\n"
" set resovler options (the same as setting $RES_OPTIONS):\n"
" timeout:sec - initial query timeout\n"
" attempts:num - number of attempt to resovle a query\n"
" ndots:num - if name has more than num dots, lookup it before search\n"
" port:num - port number for queries instead of default 53\n"
" udpbuf:num - size of UDP buffer (use EDNS0 if >512)\n"
" or query flags:\n"
" aa,nord,dnssec,do,cd - set query flag (auth-only, no recursion,\n"
" enable DNSSEC (DNSSEC Ok), check disabled)\n"
, progname, dns_version(), progname);
return 0;
default:
die(0, "try `%s -h' for help", progname);
}
argc -= optind; argv += optind;
if (!argc)
die(0, "no name(s) to query specified");
if (nns) {
/* if nameservers given as names, resolve them.
* We only allow IPv4 nameservers as names for now.
* Ok, it is easy enouth to try both AAAA and A,
* but the question is what to do by default.
*/
struct sockaddr_in sin;
int j, r = 0, opened = 0;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(dns_set_opt(NULL, DNS_OPT_PORT, -1));
dns_add_serv(NULL, NULL);
for(i = 0; i < nns; ++i) {
if (dns_pton(AF_INET, ns[i], &sin.sin_addr) <= 0) {
struct dns_rr_a4 *rr;
if (!opened) {
if (dns_open(nctx) < 0)
die(errno, "unable to initialize dns context");
opened = 1;
}
rr = dns_resolve_a4(nctx, ns[i], 0);
if (!rr)
die(0, "unable to resolve nameserver %s: %s",
ns[i], dns_strerror(dns_status(nctx)));
for(j = 0; j < rr->dnsa4_nrr; ++j) {
sin.sin_addr = rr->dnsa4_addr[j];
if ((r = dns_add_serv_s(NULL, (struct sockaddr *)&sin)) < 0)
break;
}
free(rr);
}
else
r = dns_add_serv_s(NULL, (struct sockaddr *)&sin);
if (r < 0)
die(errno, "unable to add nameserver %s",
dns_xntop(AF_INET, &sin.sin_addr));
}
}
dns_free(nctx);
fd = dns_open(NULL);
if (fd < 0)
die(errno, "unable to initialize dns context");
if (verbose > 1)
dns_set_dbgfn(NULL, dbgcb);
if (flags)
dns_set_opt(NULL, DNS_OPT_FLAGS, flags);
for (i = 0; i < argc; ++i) {
char *name = argv[i];
union {
struct in_addr addr;
struct in6_addr addr6;
} a;
unsigned char dn[DNS_MAXDN];
enum dns_type l_qtyp = 0;
int abs;
if (dns_pton(AF_INET, name, &a.addr) > 0) {
dns_a4todn(&a.addr, 0, dn, sizeof(dn));
l_qtyp = DNS_T_PTR;
abs = 1;
}
#ifdef HAVE_IPv6
else if (dns_pton(AF_INET6, name, &a.addr6) > 0) {
dns_a6todn(&a.addr6, 0, dn, sizeof(dn));
l_qtyp = DNS_T_PTR;
abs = 1;
}
#endif
else if (!dns_ptodn(name, strlen(name), dn, sizeof(dn), &abs))
die(0, "invalid name `%s'\n", name);
else
l_qtyp = DNS_T_A;
if (qtyp) l_qtyp = qtyp;
q = query_new(name, dn, l_qtyp);
if (abs) abs = DNS_NOSRCH;
if (!dns_submit_dn(NULL, dn, qcls, l_qtyp, abs, 0, dnscb, q))
dnserror(q, dns_status(NULL));
}
FD_ZERO(&fds);
now = 0;
while((i = dns_timeouts(NULL, -1, now)) > 0) {
FD_SET(fd, &fds);
tv.tv_sec = i;
tv.tv_usec = 0;
i = select(fd+1, &fds, 0, 0, &tv);
now = time(NULL);
if (i > 0) dns_ioevent(NULL, now);
}
return errors ? 1 : notfound ? 100 : 0;
}

View File

@ -0,0 +1,114 @@
/* ex-rdns.c
parallel rDNS resolver example - read IP addresses from stdin,
write domain names to stdout
Copyright (C) 2005 Michael Tokarev <mjt@corpit.ru>
This file is part of UDNS library, an async DNS stub resolver.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library, in file named COPYING.LGPL; if not,
write to the Free Software Foundation, Inc., 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/poll.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include "udns.h"
static int curq;
static const char *n2ip(const unsigned char *c) {
static char b[sizeof("255.255.255.255")];
sprintf(b, "%u.%u.%u.%u", c[0], c[1], c[2], c[3]);
return b;
}
static void dnscb(struct dns_ctx *ctx, struct dns_rr_ptr *rr, void *data) {
const char *ip = n2ip((unsigned char *)&data);
int i;
--curq;
if (rr) {
printf("%s", ip);
for(i = 0; i < rr->dnsptr_nrr; ++i)
printf(" %s", rr->dnsptr_ptr[i]);
putchar('\n');
free(rr);
}
else
fprintf(stderr, "%s: %s\n", ip, dns_strerror(dns_status(ctx)));
}
int main(int argc, char **argv) {
int c;
time_t now;
int maxq = 10;
struct pollfd pfd;
char linebuf[1024];
char *eol;
int eof;
if (dns_init(NULL, 1) < 0) {
fprintf(stderr, "unable to initialize dns library\n");
return 1;
}
while((c = getopt(argc, argv, "m:r")) != EOF) switch(c) {
case 'm': maxq = atoi(optarg); break;
case 'r':
dns_set_opt(0, DNS_OPT_FLAGS,
dns_set_opt(0, DNS_OPT_FLAGS, -1) | DNS_NORD);
break;
default: return 1;
}
if (argc != optind) return 1;
pfd.fd = dns_sock(0);
pfd.events = POLLIN;
now = time(NULL);
c = optind;
eof = 0;
while(curq || !eof) {
if (!eof && curq < maxq) {
union { struct in_addr a; void *p; } pa;
if (!fgets(linebuf, sizeof(linebuf), stdin)) {
eof = 1;
continue;
}
eol = strchr(linebuf, '\n');
if (eol) *eol = '\0';
if (!linebuf[0]) continue;
if (dns_pton(AF_INET, linebuf, &pa.a) <= 0)
fprintf(stderr, "%s: invalid address\n", linebuf);
else if (dns_submit_a4ptr(0, &pa.a, dnscb, pa.p) == 0)
fprintf(stderr, "%s: unable to submit query: %s\n",
linebuf, dns_strerror(dns_status(0)));
else
++curq;
continue;
}
if (curq) {
c = dns_timeouts(0, -1, now);
c = poll(&pfd, 1, c < 0 ? -1 : c * 1000);
now = time(NULL);
if (c)
dns_ioevent(0, now);
}
}
return 0;
}

View File

@ -0,0 +1,165 @@
/* getopt.c
* Simple getopt() implementation.
*
* Standard interface:
* extern int getopt(int argc, char *const *argv, const char *opts);
* extern int optind; current index in argv[]
* extern char *optarg; argument for the current option
* extern int optopt; the current option
* extern int opterr; to control error printing
*
* Some minor extensions:
* ignores leading `+' sign in opts[] (unemplemented GNU extension)
* handles optional arguments, in form "x::" in opts[]
* if opts[] starts with `:', will return `:' in case of missing required
* argument, instead of '?'.
*
* Compile with -DGETOPT_NO_OPTERR to never print errors internally.
* Compile with -DGETOPT_NO_STDIO to use write() calls instead of fprintf() for
* error reporting (ignored with -DGETOPT_NO_OPTERR).
* Compile with -DGETOPT_CLASS=static to get static linkage.
* Compile with -DGETOPT_MY to redefine all visible symbols to be prefixed
* with "my_", like my_getopt instead of getopt.
* Compile with -DTEST to get a test executable.
*
* Written by Michael Tokarev. Public domain.
*/
#include <string.h>
#ifndef GETOPT_CLASS
# define GETOPT_CLASS
#endif
#ifdef GETOPT_MY
# define optarg my_optarg
# define optind my_optind
# define opterr my_opterr
# define optopt my_optopt
# define getopt my_getopt
#endif
GETOPT_CLASS char *optarg /* = NULL */;
GETOPT_CLASS int optind = 1;
GETOPT_CLASS int opterr = 1;
GETOPT_CLASS int optopt;
static char *nextc /* = NULL */;
#if defined(GETOPT_NO_OPTERR)
#define printerr(argv, msg)
#elif defined(GETOPT_NO_STDIO)
extern int write(int, void *, int);
static void printerr(char *const *argv, const char *msg) {
if (opterr) {
char buf[64];
unsigned pl = strlen(argv[0]);
unsigned ml = strlen(msg);
char *p;
if (pl + /*": "*/2 + ml + /*" -- c\n"*/6 > sizeof(buf)) {
write(2, argv[0], pl);
p = buf;
}
else {
memcpy(buf, argv[0], ml);
p = buf + pl;
}
*p++ = ':'; *p++ = ' ';
memcpy(p, msg, ml); p += ml;
*p++ = ' '; *p++ = '-'; *p++ = '-'; *p++ = ' ';
*p++ = optopt;
*p++ = '\n';
write(2, buf, p - buf);
}
}
#else
#include <stdio.h>
static void printerr(char *const *argv, const char *msg) {
if (opterr)
fprintf(stderr, "%s: %s -- %c\n", argv[0], msg, optopt);
}
#endif
GETOPT_CLASS int getopt(int argc, char *const *argv, const char *opts) {
char *p;
optarg = 0;
if (*opts == '+') /* GNU extension (permutation) - isn't supported */
++opts;
if (!optind) { /* a way to reset things */
nextc = 0;
optind = 1;
}
if (!nextc || !*nextc) { /* advance to the next argv element */
/* done scanning? */
if (optind >= argc)
return -1;
/* not an optional argument */
if (argv[optind][0] != '-')
return -1;
/* bare `-' */
if (argv[optind][1] == '\0')
return -1;
/* special case `--' argument */
if (argv[optind][1] == '-' && argv[optind][2] == '\0') {
++optind;
return -1;
}
nextc = argv[optind] + 1;
}
optopt = *nextc++;
if (!*nextc)
++optind;
p = strchr(opts, optopt);
if (!p || optopt == ':') {
printerr(argv, "illegal option");
return '?';
}
if (p[1] == ':') {
if (*nextc) {
optarg = nextc;
nextc = NULL;
++optind;
}
else if (p[2] != ':') { /* required argument */
if (optind >= argc) {
printerr(argv, "option requires an argument");
return *opts == ':' ? ':' : '?';
}
else
optarg = argv[optind++];
}
}
return optopt;
}
#ifdef TEST
#include <stdio.h>
int main(int argc, char **argv) {
int c;
while((c = getopt(argc, argv, "ab:c::")) != -1) switch(c) {
case 'a':
case 'b':
case 'c':
printf("option %c %s\n", c, optarg ? optarg : "(none)");
break;
default:
return -1;
}
for(c = optind; c < argc; ++c)
printf("non-opt: %s\n", argv[c]);
return 0;
}
#endif

View File

@ -0,0 +1,327 @@
/* inet_XtoX.c
* Simple implementation of the following functions:
* inet_ntop(), inet_ntoa(), inet_pton(), inet_aton().
*
* Differences from traditional implementaitons:
* o modifies destination buffers even on error return.
* o no fancy (hex, or 1.2) input support in inet_aton()
* o inet_aton() does not accept junk after an IP address.
* o inet_ntop(AF_INET) requires at least 16 bytes in dest,
* and inet_ntop(AF_INET6) at least 40 bytes
* (traditional inet_ntop() will try to fit anyway)
*
* Compile with -Dinet_XtoX_prefix=pfx_ to have pfx_*() instead of inet_*()
* Compile with -Dinet_XtoX_no_ntop or -Dinet_XtoX_no_pton
* to disable net2str or str2net conversions.
*
* #define inet_XtoX_prototypes and #include "this_file.c"
* to get function prototypes only (but not for inet_ntoa()).
* #define inet_XtoX_decl to be `static' for static visibility,
* or use __declspec(dllexport) or somesuch...
*
* Compile with -DTEST to test against stock implementation.
*
* Written by Michael Tokarev. Public domain.
*/
#ifdef inet_XtoX_prototypes
struct in_addr;
#else
#include <errno.h>
#ifdef TEST
# include <netinet/in.h>
# include <sys/socket.h>
# include <arpa/inet.h>
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <string.h>
# undef inet_XtoX_prefix
# define inet_XtoX_prefix mjt_inet_
# undef inet_XtoX_no_ntop
# undef inet_XtoX_no_pton
#else /* !TEST */
struct in_addr { /* declare it here to avoid messing with headers */
unsigned char x[4];
};
#endif /* TEST */
#endif /* inet_XtoX_prototypes */
#ifndef inet_XtoX_prefix
# define inet_XtoX_prefix inet_
#endif
#ifndef inet_XtoX_decl
# define inet_XtoX_decl /*empty*/
#endif
#define cc2_(x,y) cc2__(x,y)
#define cc2__(x,y) x##y
#define fn(x) cc2_(inet_XtoX_prefix,x)
#ifndef inet_XtoX_no_ntop
inet_XtoX_decl const char *
fn(ntop)(int af, const void *src, char *dst, unsigned size);
#ifndef inet_XtoX_prototypes
static int mjt_ntop4(const void *_src, char *dst, int size) {
unsigned i, x, r;
char *p;
const unsigned char *s = _src;
if (size < 4*4) /* for simplicity, disallow non-max-size buffer */
return 0;
for (i = 0, p = dst; i < 4; ++i) {
if (i) *p++ = '.';
x = r = s[i];
if (x > 99) { *p++ = (char)(r / 100 + '0'); r %= 100; }
if (x > 9) { *p++ = (char)(r / 10 + '0'); r %= 10; }
*p++ = (char)(r + '0');
}
*p = '\0';
return 1;
}
static char *hexc(char *p, unsigned x) {
static char hex[16] = "0123456789abcdef";
if (x > 0x0fff) *p++ = hex[(x >>12) & 15];
if (x > 0x00ff) *p++ = hex[(x >> 8) & 15];
if (x > 0x000f) *p++ = hex[(x >> 4) & 15];
*p++ = hex[x & 15];
return p;
}
static int mjt_ntop6(const void *_src, char *dst, int size) {
unsigned i;
unsigned short w[8];
unsigned bs = 0, cs = 0;
unsigned bl = 0, cl = 0;
char *p;
const unsigned char *s = _src;
if (size < 40) /* for simplicity, disallow non-max-size buffer */
return 0;
for(i = 0; i < 8; ++i, s += 2) {
w[i] = (((unsigned short)(s[0])) << 8) | s[1];
if (!w[i]) {
if (!cl++) cs = i;
}
else {
if (cl > bl) bl = cl, bs = cs;
}
}
if (cl > bl) bl = cl, bs = cs;
p = dst;
if (bl == 1)
bl = 0;
if (bl) {
for(i = 0; i < bs; ++i) {
if (i) *p++ = ':';
p = hexc(p, w[i]);
}
*p++ = ':';
i += bl;
if (i == 8)
*p++ = ':';
}
else
i = 0;
for(; i < 8; ++i) {
if (i) *p++ = ':';
if (i == 6 && !bs && (bl == 6 || (bl == 5 && w[5] == 0xffff)))
return mjt_ntop4(s - 4, p, size - (p - dst));
p = hexc(p, w[i]);
}
*p = '\0';
return 1;
}
inet_XtoX_decl const char *
fn(ntop)(int af, const void *src, char *dst, unsigned size) {
switch(af) {
/* don't use AF_*: don't mess with headers */
case 2: /* AF_INET */ if (mjt_ntop4(src, dst, size)) return dst; break;
case 10: /* AF_INET6 */ if (mjt_ntop6(src, dst, size)) return dst; break;
default: errno = EAFNOSUPPORT; return (char*)0;
}
errno = ENOSPC;
return (char*)0;
}
inet_XtoX_decl const char *
fn(ntoa)(struct in_addr addr) {
static char buf[4*4];
mjt_ntop4(&addr, buf, sizeof(buf));
return buf;
}
#endif /* inet_XtoX_prototypes */
#endif /* inet_XtoX_no_ntop */
#ifndef inet_XtoX_no_pton
inet_XtoX_decl int fn(pton)(int af, const char *src, void *dst);
inet_XtoX_decl int fn(aton)(const char *src, struct in_addr *addr);
#ifndef inet_XtoX_prototypes
static int mjt_pton4(const char *c, void *dst) {
unsigned char *a = dst;
unsigned n, o;
for (n = 0; n < 4; ++n) {
if (*c < '0' || *c > '9')
return 0;
o = *c++ - '0';
while(*c >= '0' && *c <= '9')
if ((o = o * 10 + (*c++ - '0')) > 255)
return 0;
if (*c++ != (n == 3 ? '\0' : '.'))
return 0;
*a++ = (unsigned char)o;
}
return 1;
}
static int mjt_pton6(const char *c, void *dst) {
unsigned short w[8], *a = w, *z, *i;
unsigned v, o;
const char *sc;
unsigned char *d = dst;
if (*c != ':') z = (unsigned short*)0;
else if (*++c != ':') return 0;
else ++c, z = a;
i = 0;
for(;;) {
v = 0;
sc = c;
for(;;) {
if (*c >= '0' && *c <= '9') o = *c - '0';
else if (*c >= 'a' && *c <= 'f') o = *c - 'a' + 10;
else if (*c >= 'A' && *c <= 'F') o = *c - 'A' + 10;
else break;
v = (v << 4) | o;
if (v > 0xffff) return 0;
++c;
}
if (sc == c) {
if (z == a && !*c)
break;
else
return 0;
}
if (*c == ':') {
if (a >= w + 8)
return 0;
*a++ = v;
if (*++c == ':') {
if (z)
return 0;
z = a;
if (!*++c)
break;
}
}
else if (!*c) {
if (a >= w + 8)
return 0;
*a++ = v;
break;
}
else if (*c == '.') {
if (a > w + 6)
return 0;
if (!mjt_pton4(sc, d))
return 0;
*a++ = ((unsigned)(d[0]) << 8) | d[1];
*a++ = ((unsigned)(d[2]) << 8) | d[3];
break;
}
else
return 0;
}
v = w + 8 - a;
if ((v && !z) || (!v && z))
return 0;
for(i = w; ; ++i) {
if (i == z)
while(v--) { *d++ = '\0'; *d++ = '\0'; }
if (i >= a)
break;
*d++ = (unsigned char)((*i >> 8) & 255);
*d++ = (unsigned char)(*i & 255);
}
return 1;
}
inet_XtoX_decl int fn(pton)(int af, const char *src, void *dst) {
switch(af) {
/* don't use AF_*: don't mess with headers */
case 2 /* AF_INET */: return mjt_pton4(src, dst);
case 10 /* AF_INET6 */: return mjt_pton6(src, dst);
default: errno = EAFNOSUPPORT; return -1;
}
}
inet_XtoX_decl int fn(aton)(const char *src, struct in_addr *addr) {
return mjt_pton4(src, addr);
}
#endif /* inet_XtoX_prototypes */
#endif /* inet_XtoX_no_pton */
#ifdef TEST
int main(int argc, char **argv) {
int i;
char n0[16], n1[16];
char p0[64], p1[64];
int af = AF_INET;
int pl = sizeof(p0);
int r0, r1;
const char *s0, *s1;
while((i = getopt(argc, argv, "46a:p:")) != EOF) switch(i) {
case '4': af = AF_INET; break;
case '6': af = AF_INET6; break;
case 'a': case 'p': pl = atoi(optarg); break;
default: return 1;
}
for(i = optind; i < argc; ++i) {
char *a = argv[i];
printf("%s:\n", a);
r0 = inet_pton(af, a, n0);
printf(" p2n stock: %s\n",
(r0 < 0 ? "(notsupp)" : !r0 ? "(inval)" : fn(ntop)(af,n0,p0,sizeof(p0))));
r1 = fn(pton)(af, a, n1);
printf(" p2n this : %s\n",
(r1 < 0 ? "(notsupp)" : !r1 ? "(inval)" : fn(ntop)(af,n1,p1,sizeof(p1))));
if ((r0 > 0) != (r1 > 0) ||
(r0 > 0 && r1 > 0 && memcmp(n0, n1, af == AF_INET ? 4 : 16) != 0))
printf(" DIFFER!\n");
s0 = inet_ntop(af, n1, p0, pl);
printf(" n2p stock: %s\n", s0 ? s0 : "(inval)");
s1 = fn(ntop)(af, n1, p1, pl);
printf(" n2p this : %s\n", s1 ? s1 : "(inval)");
if ((s0 != 0) != (s1 != 0) ||
(s0 && s1 && strcmp(s0, s1) != 0))
printf(" DIFFER!\n");
}
return 0;
}
#endif /* TEST */

View File

@ -0,0 +1,151 @@
.\" rblcheck.1
.\" rblckeck manpage
.\"
.\" Copyright (C) 2005 Michael Tokarev <mjt@corpit.ru>
.\" This file is part of UDNS library, an async DNS stub resolver.
.\"
.\" This library is free software; you can redistribute it and/or
.\" modify it under the terms of the GNU Lesser General Public
.\" License as published by the Free Software Foundation; either
.\" version 2.1 of the License, or (at your option) any later version.
.\"
.\" This library is distributed in the hope that it will be useful,
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
.\" Lesser General Public License for more details.
.\"
.\" You should have received a copy of the GNU Lesser General Public
.\" License along with this library, in file named COPYING.LGPL; if not,
.\" write to the Free Software Foundation, Inc., 59 Temple Place,
.\" Suite 330, Boston, MA 02111-1307 USA
.TH rblckeck 1 "Apr 2005" "User Utilities"
.SH NAME
rblckeck \- DNSBL lookup utility
.SH SYNOPSYS
.B rblcheck
.RB [\| \-s
.IR zone \|]
.RB [\| \-S
.IR zone\-file \|]
.RB [\| \-c \|]
.RB [\| \-tmvq \|]
.RB [\| \-n
.IR nsaddr \|]
.IR address \|.\|.\|.
.SH DESCRIPTION
.B rblcheck
is a simple command-line to perform DNSBL (DNS-based blocklists) lookups.
For every IP address (or a name, in which case it will be resolved to an
address first), the utility verifies whenever it is listed in a (list of)
DNS blocklists specified with
.B \-s
or
.B \-S
options, optionally obtains text assotiated with the listing (usually it
is either some description about the reason of the listing or an URL
referring to such a description), and displays results on standard output.
.PP
The program is implemented on top of
.BR udns (3)
library.
.SH OPTIONS
The following options are recognized by
.BR rblcheck :
.TP
.B \-s \fIzone\fR
add the given \fIzone\fR DNSBL name to the list of active zones.
.TP
.B \-S \fIzone-file\fR
add list of zones from the named \fIzone-file\fR to the list of
active zones (the file specifies one zone as the first word on a
line, empty lines and lines starting with `#' character are ignored).
.TP
.B \-c
reset active zone list.
.TP
.B \-v
be more verbose, produce more detailed output.
.TP
.B \-q
the opposite for \fB\-v\fR -- produce less detailed output.
.TP
.B \-t
obtain text for listed addresses.
.TP
.B \-n \fInsaddr\fR
Use the given nameserver (given as IPv4 or IPv6 address) instead of the
default. The same effect may be achieved by setting $NSCACHEIP environment
variable.
.TP
.B \-m
stop after first hit, ie after the first address which is found to be
listed.
.TP
.B \-h
print short help and exit.
.PP
If no
.BR \-s ,
.BR \-S
and
.B \-c
options are given,
.B rblcheck
will try to obtain list of zones using $RBLCHECK_ZONES environment variable,
or ~/.rblcheckrc, or /etc/rblckechrc files, in that order. If no zones are
found, it will exit unsuccessefully.
.SH "RETURN VALUE"
When no addresses given are listed and no errors occured,
.B rblcheck
exits with code 0. If at least one address is listed,
.B rblcheck
returns 100. In case of DNS errors,
.B rblcheck
returns 2.
.SH ENVIRONMENT
.TP
.B $RBLCHECK_ZONES
if no
.BR \-s ,
.B \-S
or
.B \-c
option is given,
.B rblcheck
tries this variable to obtain list of DNSBL zones to check against.
.SH FILES
.TP
$HOME/.rblcheckrc and /etc/rblcheckrc
if no
.BR \-s ,
.B \-S
or
.B \-c
option is given, and no $RBLCHECK_ZONES environment variable is set,
.B rblcheck
will try the two files (the first one that exists) to obtain list of
DNSBL zones to check against.
Each line specifies one zone (only first word in each line is used).
Empty lines and lines starting with `#' character are ignored.
.SH "SEE ALSO"
.BR dnsget (1)
.BR resolv.conf (5)
.BR udns (3).
.SH AUTHOR
This program and manual pages are written by Michael Tokarev.

View File

@ -0,0 +1,378 @@
/* rblcheck.c
dnsbl (rbl) checker application
Copyright (C) 2005 Michael Tokarev <mjt@corpit.ru>
This file is part of UDNS library, an async DNS stub resolver.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library, in file named COPYING.LGPL; if not,
write to the Free Software Foundation, Inc., 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef WINDOWS
# include <winsock2.h>
#else
# include <unistd.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <netinet/in.h>
#endif
#include <time.h>
#include <errno.h>
#include <stdarg.h>
#include "udns.h"
#ifndef HAVE_GETOPT
# include "getopt.c"
#endif
static const char *version = "udns-rblcheck 0.4";
static char *progname;
static void error(int die, const char *fmt, ...) {
va_list ap;
fprintf(stderr, "%s: ", progname);
va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap);
putc('\n', stderr);
fflush(stderr);
if (die)
exit(1);
}
struct rblookup {
struct ipcheck *parent;
struct in_addr key;
const char *zone;
struct dns_rr_a4 *addr;
struct dns_rr_txt *txt;
};
struct ipcheck {
const char *name;
int naddr;
int listed;
struct rblookup *lookup;
};
#define notlisted ((void*)1)
static int nzones, nzalloc;
static const char **zones;
static int do_txt;
static int stopfirst;
static int verbose = 1;
/* verbosity level:
* <0 - only bare As/TXTs
* 0 - what RBL result
* 1(default) - what is listed by RBL: result
* 2 - what is[not ]listed by RBL: result, name lookups
*/
static int listed;
static int failures;
static void *ecalloc(int size, int cnt) {
void *t = calloc(size, cnt);
if (!t)
error(1, "out of memory");
return t;
}
static void addzone(const char *zone) {
if (nzones >= nzalloc) {
const char **zs = (const char**)ecalloc(sizeof(char*), (nzalloc += 16));
if (zones) {
memcpy(zs, zones, nzones * sizeof(char*));
free(zones);
}
zones = zs;
}
zones[nzones++] = zone;
}
static int addzonefile(const char *fname) {
FILE *f = fopen(fname, "r");
char linebuf[2048];
if (!f)
return 0;
while(fgets(linebuf, sizeof(linebuf), f)) {
char *p = linebuf, *e;
while(*p == ' ' || *p == '\t') ++p;
if (*p == '#' || *p == '\n') continue;
e = p;
while(*e && *e != ' ' && *e != '\t' && *e != '\n')
++e;
*e++ = '\0';
p = memcpy(ecalloc(e - p, 1), p, e - p); // strdup
addzone(p);
}
fclose(f);
return 1;
}
static void dnserror(struct rblookup *ipl, const char *what) {
char buf[4*4];
error(0, "unable to %s for %s (%s): %s",
what, dns_ntop(AF_INET, &ipl->key, buf, sizeof(buf)),
ipl->zone, dns_strerror(dns_status(0)));
++failures;
}
static void display_result(struct ipcheck *ipc) {
int j;
struct rblookup *l, *le;
char buf[4*4];
if (!ipc->naddr) return;
for (l = ipc->lookup, le = l + nzones * ipc->naddr; l < le; ++l) {
if (!l->addr) continue;
if (verbose < 2 && l->addr == notlisted) continue;
if (verbose >= 0) {
dns_ntop(AF_INET, &l->key, buf, sizeof(buf));
if (ipc->name) printf("%s[%s]", ipc->name, buf);
else printf("%s", buf);
}
if (l->addr == notlisted) {
printf(" is NOT listed by %s\n", l->zone);
continue;
}
else if (verbose >= 1)
printf(" is listed by %s: ", l->zone);
else if (verbose >= 0)
printf(" %s ", l->zone);
if (verbose >= 1 || !do_txt)
for (j = 0; j < l->addr->dnsa4_nrr; ++j)
printf("%s%s", j ? " " : "",
dns_ntop(AF_INET, &l->addr->dnsa4_addr[j], buf, sizeof(buf)));
if (!do_txt) ;
else if (l->txt) {
for(j = 0; j < l->txt->dnstxt_nrr; ++j) {
unsigned char *t = l->txt->dnstxt_txt[j].txt;
unsigned char *e = t + l->txt->dnstxt_txt[j].len;
printf("%s\"", verbose > 0 ? "\n\t" : j ? " " : "");
while(t < e) {
if (*t < ' ' || *t >= 127) printf("\\x%02x", *t);
else if (*t == '\\' || *t == '"') printf("\\%c", *t);
else putchar(*t);
++t;
}
putchar('"');
}
free(l->txt);
}
else
printf("%s<no text available>", verbose > 0 ? "\n\t" : "");
free(l->addr);
putchar('\n');
}
free(ipc->lookup);
}
static void txtcb(struct dns_ctx *ctx, struct dns_rr_txt *r, void *data) {
struct rblookup *ipl = data;
if (r) {
ipl->txt = r;
++ipl->parent->listed;
}
else if (dns_status(ctx) != DNS_E_NXDOMAIN)
dnserror(ipl, "lookup DNSBL TXT record");
}
static void a4cb(struct dns_ctx *ctx, struct dns_rr_a4 *r, void *data) {
struct rblookup *ipl = data;
if (r) {
ipl->addr = r;
++listed;
if (do_txt) {
if (dns_submit_a4dnsbl_txt(0, &ipl->key, ipl->zone, txtcb, ipl))
return;
dnserror(ipl, "submit DNSBL TXT record");
}
++ipl->parent->listed;
}
else if (dns_status(ctx) != DNS_E_NXDOMAIN)
dnserror(ipl, "lookup DNSBL A record");
else
ipl->addr = notlisted;
}
static int
submit_a_queries(struct ipcheck *ipc,
int naddr, const struct in_addr *addr) {
int z, a;
struct rblookup *rl = ecalloc(sizeof(*rl), nzones * naddr);
ipc->lookup = rl;
ipc->naddr = naddr;
for(a = 0; a < naddr; ++a) {
for(z = 0; z < nzones; ++z) {
rl->key = addr[a];
rl->zone = zones[z];
rl->parent = ipc;
if (!dns_submit_a4dnsbl(0, &rl->key, rl->zone, a4cb, rl))
dnserror(rl, "submit DNSBL A query");
++rl;
}
}
return 0;
}
static void namecb(struct dns_ctx *ctx, struct dns_rr_a4 *rr, void *data) {
struct ipcheck *ipc = data;
if (rr) {
submit_a_queries(ipc, rr->dnsa4_nrr, rr->dnsa4_addr);
free(rr);
}
else {
error(0, "unable to lookup `%s': %s",
ipc->name, dns_strerror(dns_status(ctx)));
++failures;
}
}
static int submit(struct ipcheck *ipc) {
struct in_addr addr;
if (dns_pton(AF_INET, ipc->name, &addr) > 0) {
submit_a_queries(ipc, 1, &addr);
ipc->name = NULL;
}
else if (!dns_submit_a4(0, ipc->name, 0, namecb, ipc)) {
error(0, "unable to submit name query for %s: %s\n",
ipc->name, dns_strerror(dns_status(0)));
++failures;
}
return 0;
}
static void waitdns(struct ipcheck *ipc) {
struct timeval tv;
fd_set fds;
int c;
int fd = dns_sock(NULL);
time_t now = 0;
FD_ZERO(&fds);
while((c = dns_timeouts(NULL, -1, now)) > 0) {
FD_SET(fd, &fds);
tv.tv_sec = c;
tv.tv_usec = 0;
c = select(fd+1, &fds, NULL, NULL, &tv);
now = time(NULL);
if (c > 0)
dns_ioevent(NULL, now);
if (stopfirst && ipc->listed)
break;
}
}
int main(int argc, char **argv) {
int c;
struct ipcheck ipc;
char *nameserver = NULL;
int zgiven = 0;
if (!(progname = strrchr(argv[0], '/'))) progname = argv[0];
else argv[0] = ++progname;
while((c = getopt(argc, argv, "hqtvms:S:cn:")) != EOF) switch(c) {
case 's': ++zgiven; addzone(optarg); break;
case 'S':
++zgiven;
if (addzonefile(optarg)) break;
error(1, "unable to read zonefile `%s'", optarg);
case 'c': ++zgiven; nzones = 0; break;
case 'q': --verbose; break;
case 'v': ++verbose; break;
case 't': do_txt = 1; break;
case 'n': nameserver = optarg; break;
case 'm': ++stopfirst; break;
case 'h':
printf("%s: %s (udns library version %s).\n",
progname, version, dns_version());
printf("Usage is: %s [options] address..\n", progname);
printf(
"Where options are:\n"
" -h - print this help and exit\n"
" -s service - add the service (DNSBL zone) to the serice list\n"
" -S service-file - add the DNSBL zone(s) read from the given file\n"
" -c - clear service list\n"
" -v - increase verbosity level (more -vs => more verbose)\n"
" -q - decrease verbosity level (opposite of -v)\n"
" -t - obtain and print TXT records if any\n"
" -m - stop checking after first address match in any list\n"
" -n ipaddr - use the given nameserver instead of the default\n"
"(if no -s or -S option is given, use $RBLCHECK_ZONES, ~/.rblcheckrc\n"
"or /etc/rblcheckrc in that order)\n"
);
return 0;
default:
error(1, "use `%s -h' for help", progname);
}
if (!zgiven) {
char *s = getenv("RBLCHECK_ZONES");
if (s) {
char *k;
s = strdup(s);
for(k = strtok(s, " \t"); k; k = strtok(NULL, " \t"))
addzone(k);
free(s);
}
else { /* probably worthless on windows? */
char *path;
char *home = getenv("HOME");
if (!home) home = ".";
path = malloc(strlen(home) + 1 + sizeof(".rblcheckrc"));
sprintf(path, "%s/.rblcheckrc", home);
if (!addzonefile(path))
addzonefile("/etc/rblcheckrc");
free(path);
}
}
if (!nzones)
error(1, "no service (zone) list specified (-s or -S option)");
argv += optind;
argc -= optind;
if (!argc)
return 0;
if (dns_init(NULL, 0) < 0)
error(1, "unable to initialize DNS library: %s", strerror(errno));
if (nameserver) {
dns_add_serv(NULL, NULL);
if (dns_add_serv(NULL, nameserver) < 0)
error(1, "wrong IP address for a nameserver: `%s'", nameserver);
}
if (dns_open(NULL) < 0)
error(1, "unable to initialize DNS library: %s", strerror(errno));
for (c = 0; c < argc; ++c) {
if (c && (verbose > 1 || (verbose == 1 && do_txt))) putchar('\n');
memset(&ipc, 0, sizeof(ipc));
ipc.name = argv[c];
submit(&ipc);
waitdns(&ipc);
display_result(&ipc);
if (stopfirst > 1 && listed) break;
}
return listed ? 100 : failures ? 2 : 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,778 @@
/* udns.h
header file for the UDNS library.
Copyright (C) 2005 Michael Tokarev <mjt@corpit.ru>
This file is part of UDNS library, an async DNS stub resolver.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library, in file named COPYING.LGPL; if not,
write to the Free Software Foundation, Inc., 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef UDNS_VERSION /* include guard */
#define UDNS_VERSION "0.4"
#ifdef WINDOWS
# ifdef UDNS_DYNAMIC_LIBRARY
# ifdef DNS_LIBRARY_BUILD
# define UDNS_API __declspec(dllexport)
# define UDNS_DATA_API __declspec(dllexport)
# else
# define UDNS_API __declspec(dllimport)
# define UDNS_DATA_API __declspec(dllimport)
# endif
# endif
#endif
#ifndef UDNS_API
# define UDNS_API
#endif
#ifndef UDNS_DATA_API
# define UDNS_DATA_API
#endif
#include <sys/types.h> /* for time_t */
#ifdef __cplusplus
extern "C" {
#endif
/* forward declarations if sockets stuff isn't #include'd */
struct in_addr;
struct in6_addr;
struct sockaddr;
/**************************************************************************/
/**************** Common definitions **************************************/
UDNS_API const char *
dns_version(void);
struct dns_ctx;
struct dns_query;
/* shorthand for [const] unsigned char */
typedef unsigned char dnsc_t;
typedef const unsigned char dnscc_t;
#define DNS_MAXDN 255 /* max DN length */
#define DNS_DNPAD 1 /* padding for DN buffers */
#define DNS_MAXLABEL 63 /* max DN label length */
#define DNS_MAXNAME 1024 /* max asciiz domain name length */
#define DNS_HSIZE 12 /* DNS packet header size */
#define DNS_PORT 53 /* default domain port */
#define DNS_MAXSERV 6 /* max servers to consult */
#define DNS_MAXPACKET 512 /* max traditional-DNS UDP packet size */
#define DNS_EDNS0PACKET 4096 /* EDNS0 packet size to use */
enum dns_class { /* DNS RR Classes */
DNS_C_INVALID = 0, /* invalid class */
DNS_C_IN = 1, /* Internet */
DNS_C_CH = 3, /* CHAOS */
DNS_C_HS = 4, /* HESIOD */
DNS_C_ANY = 255 /* wildcard */
};
enum dns_type { /* DNS RR Types */
DNS_T_INVALID = 0, /* Cookie. */
DNS_T_A = 1, /* Host address. */
DNS_T_NS = 2, /* Authoritative server. */
DNS_T_MD = 3, /* Mail destination. */
DNS_T_MF = 4, /* Mail forwarder. */
DNS_T_CNAME = 5, /* Canonical name. */
DNS_T_SOA = 6, /* Start of authority zone. */
DNS_T_MB = 7, /* Mailbox domain name. */
DNS_T_MG = 8, /* Mail group member. */
DNS_T_MR = 9, /* Mail rename name. */
DNS_T_NULL = 10, /* Null resource record. */
DNS_T_WKS = 11, /* Well known service. */
DNS_T_PTR = 12, /* Domain name pointer. */
DNS_T_HINFO = 13, /* Host information. */
DNS_T_MINFO = 14, /* Mailbox information. */
DNS_T_MX = 15, /* Mail routing information. */
DNS_T_TXT = 16, /* Text strings. */
DNS_T_RP = 17, /* Responsible person. */
DNS_T_AFSDB = 18, /* AFS cell database. */
DNS_T_X25 = 19, /* X_25 calling address. */
DNS_T_ISDN = 20, /* ISDN calling address. */
DNS_T_RT = 21, /* Router. */
DNS_T_NSAP = 22, /* NSAP address. */
DNS_T_NSAP_PTR = 23, /* Reverse NSAP lookup (deprecated). */
DNS_T_SIG = 24, /* Security signature. */
DNS_T_KEY = 25, /* Security key. */
DNS_T_PX = 26, /* X.400 mail mapping. */
DNS_T_GPOS = 27, /* Geographical position (withdrawn). */
DNS_T_AAAA = 28, /* Ip6 Address. */
DNS_T_LOC = 29, /* Location Information. */
DNS_T_NXT = 30, /* Next domain (security). */
DNS_T_EID = 31, /* Endpoint identifier. */
DNS_T_NIMLOC = 32, /* Nimrod Locator. */
DNS_T_SRV = 33, /* Server Selection. */
DNS_T_ATMA = 34, /* ATM Address */
DNS_T_NAPTR = 35, /* Naming Authority PoinTeR */
DNS_T_KX = 36, /* Key Exchange */
DNS_T_CERT = 37, /* Certification record */
DNS_T_A6 = 38, /* IPv6 address (deprecates AAAA) */
DNS_T_DNAME = 39, /* Non-terminal DNAME (for IPv6) */
DNS_T_SINK = 40, /* Kitchen sink (experimentatl) */
DNS_T_OPT = 41, /* EDNS0 option (meta-RR) */
DNS_T_DS = 43, /* DNSSEC */
DNS_T_SSHFP = 44,
DNS_T_IPSECKEY = 45,
DNS_T_RRSIG = 46, /* DNSSEC */
DNS_T_NSEC = 47, /* DNSSEC */
DNS_T_DNSKEY = 48,
DNS_T_DHCID = 49,
DNS_T_NSEC3 = 50,
DNS_T_NSEC3PARAMS = 51,
DNS_T_TALINK = 58, /* draft-ietf-dnsop-trust-history */
DNS_T_SPF = 99,
DNS_T_UINFO = 100,
DNS_T_UID = 101,
DNS_T_GID = 102,
DNS_T_UNSPEC = 103,
DNS_T_TSIG = 250, /* Transaction signature. */
DNS_T_IXFR = 251, /* Incremental zone transfer. */
DNS_T_AXFR = 252, /* Transfer zone of authority. */
DNS_T_MAILB = 253, /* Transfer mailbox records. */
DNS_T_MAILA = 254, /* Transfer mail agent records. */
DNS_T_ANY = 255, /* Wildcard match. */
DNS_T_ZXFR = 256, /* BIND-specific, nonstandard. */
DNS_T_DLV = 32769, /* RFC 4431, 5074, DNSSEC Lookaside Validation */
DNS_T_MAX = 65536
};
/**************************************************************************/
/**************** Domain Names (DNs) **************************************/
/* return length of the DN */
UDNS_API unsigned
dns_dnlen(dnscc_t *dn);
/* return #of labels in a DN */
UDNS_API unsigned
dns_dnlabels(dnscc_t *dn);
/* lower- and uppercase single DN char */
#define DNS_DNLC(c) ((c) >= 'A' && (c) <= 'Z' ? (c) - 'A' + 'a' : (c))
#define DNS_DNUC(c) ((c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 'A' : (c))
/* compare the DNs, return dnlen of equal or 0 if not */
UDNS_API unsigned
dns_dnequal(dnscc_t *dn1, dnscc_t *dn2);
/* copy one DN to another, size checking */
UDNS_API unsigned
dns_dntodn(dnscc_t *sdn, dnsc_t *ddn, unsigned ddnsiz);
/* convert asciiz string of length namelen (0 to use strlen) to DN */
UDNS_API int
dns_ptodn(const char *name, unsigned namelen,
dnsc_t *dn, unsigned dnsiz, int *isabs);
/* simpler form of dns_ptodn() */
#define dns_sptodn(name,dn,dnsiz) dns_ptodn((name),0,(dn),(dnsiz),0)
UDNS_DATA_API extern dnscc_t dns_inaddr_arpa_dn[14];
#define DNS_A4RSIZE 30
UDNS_API int
dns_a4todn(const struct in_addr *addr, dnscc_t *tdn,
dnsc_t *dn, unsigned dnsiz);
UDNS_API int
dns_a4ptodn(const struct in_addr *addr, const char *tname,
dnsc_t *dn, unsigned dnsiz);
UDNS_API dnsc_t *
dns_a4todn_(const struct in_addr *addr, dnsc_t *dn, dnsc_t *dne);
UDNS_DATA_API extern dnscc_t dns_ip6_arpa_dn[10];
#define DNS_A6RSIZE 74
UDNS_API int
dns_a6todn(const struct in6_addr *addr, dnscc_t *tdn,
dnsc_t *dn, unsigned dnsiz);
UDNS_API int
dns_a6ptodn(const struct in6_addr *addr, const char *tname,
dnsc_t *dn, unsigned dnsiz);
UDNS_API dnsc_t *
dns_a6todn_(const struct in6_addr *addr, dnsc_t *dn, dnsc_t *dne);
/* convert DN into asciiz string */
UDNS_API int
dns_dntop(dnscc_t *dn, char *name, unsigned namesiz);
/* convert DN into asciiz string, using static buffer (NOT thread-safe!) */
UDNS_API const char *
dns_dntosp(dnscc_t *dn);
/* return buffer size (incl. null byte) required for asciiz form of a DN */
UDNS_API unsigned
dns_dntop_size(dnscc_t *dn);
/* either wrappers or reimplementations for inet_ntop() and inet_pton() */
UDNS_API const char *dns_ntop(int af, const void *src, char *dst, int size);
UDNS_API int dns_pton(int af, const char *src, void *dst);
/**************************************************************************/
/**************** DNS raw packet layout ***********************************/
enum dns_rcode { /* reply codes */
DNS_R_NOERROR = 0, /* ok, no error */
DNS_R_FORMERR = 1, /* format error */
DNS_R_SERVFAIL = 2, /* server failed */
DNS_R_NXDOMAIN = 3, /* domain does not exists */
DNS_R_NOTIMPL = 4, /* not implemented */
DNS_R_REFUSED = 5, /* query refused */
/* these are for BIND_UPDATE */
DNS_R_YXDOMAIN = 6, /* Name exists */
DNS_R_YXRRSET = 7, /* RRset exists */
DNS_R_NXRRSET = 8, /* RRset does not exist */
DNS_R_NOTAUTH = 9, /* Not authoritative for zone */
DNS_R_NOTZONE = 10, /* Zone of record different from zone section */
/*ns_r_max = 11,*/
/* The following are TSIG extended errors */
DNS_R_BADSIG = 16,
DNS_R_BADKEY = 17,
DNS_R_BADTIME = 18
};
static __inline unsigned dns_get16(dnscc_t *s) {
return ((unsigned)s[0]<<8) | s[1];
}
static __inline unsigned dns_get32(dnscc_t *s) {
return ((unsigned)s[0]<<24) | ((unsigned)s[1]<<16)
| ((unsigned)s[2]<<8) | s[3];
}
static __inline dnsc_t *dns_put16(dnsc_t *d, unsigned n) {
*d++ = (dnsc_t)((n >> 8) & 255); *d++ = (dnsc_t)(n & 255); return d;
}
static __inline dnsc_t *dns_put32(dnsc_t *d, unsigned n) {
*d++ = (dnsc_t)((n >> 24) & 255); *d++ = (dnsc_t)((n >> 16) & 255);
*d++ = (dnsc_t)((n >> 8) & 255); *d++ = (dnsc_t)(n & 255);
return d;
}
/* DNS Header layout */
enum {
/* bytes 0:1 - query ID */
DNS_H_QID1 = 0,
DNS_H_QID2 = 1,
DNS_H_QID = DNS_H_QID1,
#define dns_qid(pkt) dns_get16((pkt)+DNS_H_QID)
/* byte 2: flags1 */
DNS_H_F1 = 2,
DNS_HF1_QR = 0x80, /* query response flag */
#define dns_qr(pkt) ((pkt)[DNS_H_F1]&DNS_HF1_QR)
DNS_HF1_OPCODE = 0x78, /* opcode, 0 = query */
#define dns_opcode(pkt) (((pkt)[DNS_H_F1]&DNS_HF1_OPCODE)>>3)
DNS_HF1_AA = 0x04, /* auth answer */
#define dns_aa(pkt) ((pkt)[DNS_H_F1]&DNS_HF1_AA)
DNS_HF1_TC = 0x02, /* truncation flag */
#define dns_tc(pkt) ((pkt)[DNS_H_F1]&DNS_HF1_TC)
DNS_HF1_RD = 0x01, /* recursion desired (may be set in query) */
#define dns_rd(pkt) ((pkt)[DNS_H_F1]&DNS_HF1_RD)
/* byte 3: flags2 */
DNS_H_F2 = 3,
DNS_HF2_RA = 0x80, /* recursion available */
#define dns_ra(pkt) ((pkt)[DNS_H_F2]&DNS_HF2_RA)
DNS_HF2_Z = 0x40, /* reserved */
DNS_HF2_AD = 0x20, /* DNSSEC: authentic data */
#define dns_ad(pkt) ((pkt)[DNS_H_F2]&DNS_HF2_AD)
DNS_HF2_CD = 0x10, /* DNSSEC: checking disabled */
#define dns_cd(pkt) ((pkt)[DNS_H_F2]&DNS_HF2_CD)
DNS_HF2_RCODE = 0x0f, /* response code, DNS_R_XXX above */
#define dns_rcode(pkt) ((pkt)[DNS_H_F2]&DNS_HF2_RCODE)
/* bytes 4:5: qdcount, numqueries */
DNS_H_QDCNT1 = 4,
DNS_H_QDCNT2 = 5,
DNS_H_QDCNT = DNS_H_QDCNT1,
#define dns_numqd(pkt) dns_get16((pkt)+4)
/* bytes 6:7: ancount, numanswers */
DNS_H_ANCNT1 = 6,
DNS_H_ANCNT2 = 7,
DNS_H_ANCNT = DNS_H_ANCNT1,
#define dns_numan(pkt) dns_get16((pkt)+6)
/* bytes 8:9: nscount, numauthority */
DNS_H_NSCNT1 = 8,
DNS_H_NSCNT2 = 9,
DNS_H_NSCNT = DNS_H_NSCNT1,
#define dns_numns(pkt) dns_get16((pkt)+8)
/* bytes 10:11: arcount, numadditional */
DNS_H_ARCNT1 = 10,
DNS_H_ARCNT2 = 11,
DNS_H_ARCNT = DNS_H_ARCNT1,
#define dns_numar(pkt) dns_get16((pkt)+10)
#define dns_payload(pkt) ((pkt)+DNS_HSIZE)
/* EDNS0 (OPT RR) flags (Ext. Flags) */
DNS_EF1_DO = 0x80, /* DNSSEC OK */
};
/* packet buffer: start at pkt, end before pkte, current pos *curp.
* extract a DN and set *curp to the next byte after DN in packet.
* return -1 on error, 0 if dnsiz is too small, or dnlen on ok.
*/
UDNS_API int
dns_getdn(dnscc_t *pkt, dnscc_t **curp, dnscc_t *end,
dnsc_t *dn, unsigned dnsiz);
/* skip the DN at position cur in packet ending before pkte,
* return pointer to the next byte after the DN or NULL on error */
UDNS_API dnscc_t *
dns_skipdn(dnscc_t *end, dnscc_t *cur);
struct dns_rr { /* DNS Resource Record */
dnsc_t dnsrr_dn[DNS_MAXDN]; /* the DN of the RR */
enum dns_class dnsrr_cls; /* Class */
enum dns_type dnsrr_typ; /* Type */
unsigned dnsrr_ttl; /* Time-To-Live (TTL) */
unsigned dnsrr_dsz; /* data size */
dnscc_t *dnsrr_dptr; /* pointer to start of data */
dnscc_t *dnsrr_dend; /* past end of data */
};
struct dns_parse { /* RR/packet parsing state */
dnscc_t *dnsp_pkt; /* start of the packet */
dnscc_t *dnsp_end; /* end of the packet */
dnscc_t *dnsp_cur; /* current packet position */
dnscc_t *dnsp_ans; /* start of answer section */
int dnsp_rrl; /* number of RRs left to go */
int dnsp_nrr; /* RR count so far */
unsigned dnsp_ttl; /* TTL value so far */
dnscc_t *dnsp_qdn; /* the RR DN we're looking for */
enum dns_class dnsp_qcls; /* RR class we're looking for or 0 */
enum dns_type dnsp_qtyp; /* RR type we're looking for or 0 */
dnsc_t dnsp_dnbuf[DNS_MAXDN]; /* domain buffer */
};
/* initialize the parse structure */
UDNS_API void
dns_initparse(struct dns_parse *p, dnscc_t *qdn,
dnscc_t *pkt, dnscc_t *cur, dnscc_t *end);
/* search next RR, <0=error, 0=no more RRs, >0 = found. */
UDNS_API int
dns_nextrr(struct dns_parse *p, struct dns_rr *rr);
UDNS_API void
dns_rewind(struct dns_parse *p, dnscc_t *qdn);
/**************************************************************************/
/**************** Resolver Context ****************************************/
/* default resolver context */
UDNS_DATA_API extern struct dns_ctx dns_defctx;
/* reset resolver context to default state, close it if open, drop queries */
UDNS_API void
dns_reset(struct dns_ctx *ctx);
/* reset resolver context and read in system configuration */
UDNS_API int
dns_init(struct dns_ctx *ctx, int do_open);
/* return new resolver context with the same settings as copy */
UDNS_API struct dns_ctx *
dns_new(const struct dns_ctx *copy);
/* free resolver context returned by dns_new(); all queries are dropped */
UDNS_API void
dns_free(struct dns_ctx *ctx);
/* add nameserver for a resolver context (or reset nslist if serv==NULL) */
UDNS_API int
dns_add_serv(struct dns_ctx *ctx, const char *serv);
/* add nameserver using struct sockaddr structure (with ports) */
UDNS_API int
dns_add_serv_s(struct dns_ctx *ctx, const struct sockaddr *sa);
/* add search list element for a resolver context (or reset it if srch==NULL) */
UDNS_API int
dns_add_srch(struct dns_ctx *ctx, const char *srch);
/* set options for a resolver context */
UDNS_API int
dns_set_opts(struct dns_ctx *ctx, const char *opts);
enum dns_opt { /* options */
DNS_OPT_FLAGS, /* flags, DNS_F_XXX */
DNS_OPT_TIMEOUT, /* timeout in secounds */
DNS_OPT_NTRIES, /* number of retries */
DNS_OPT_NDOTS, /* ndots */
DNS_OPT_UDPSIZE, /* EDNS0 UDP size */
DNS_OPT_PORT, /* port to use */
};
/* set or get (if val<0) an option */
UDNS_API int
dns_set_opt(struct dns_ctx *ctx, enum dns_opt opt, int val);
enum dns_flags {
DNS_NOSRCH = 0x00010000, /* do not perform search */
DNS_NORD = 0x00020000, /* request no recursion */
DNS_AAONLY = 0x00040000, /* set AA flag in queries */
DNS_SET_DO = 0x00080000, /* set EDNS0 "DO" bit (DNSSEC OK) */
DNS_SET_CD = 0x00100000, /* set CD bit (DNSSEC: checking disabled) */
};
/* set the debug function pointer */
typedef void
(dns_dbgfn)(int code, const struct sockaddr *sa, unsigned salen,
dnscc_t *pkt, int plen,
const struct dns_query *q, void *data);
UDNS_API void
dns_set_dbgfn(struct dns_ctx *ctx, dns_dbgfn *dbgfn);
/* open and return UDP socket */
UDNS_API int
dns_open(struct dns_ctx *ctx);
/* return UDP socket or -1 if not open */
UDNS_API int
dns_sock(const struct dns_ctx *ctx);
/* close the UDP socket */
UDNS_API void
dns_close(struct dns_ctx *ctx);
/* return number of requests queued */
UDNS_API int
dns_active(const struct dns_ctx *ctx);
/* return status of the last operation */
UDNS_API int
dns_status(const struct dns_ctx *ctx);
UDNS_API void
dns_setstatus(struct dns_ctx *ctx, int status);
/* handle I/O event on UDP socket */
UDNS_API void
dns_ioevent(struct dns_ctx *ctx, time_t now);
/* process any timeouts, return time in secounds to the
* next timeout (or -1 if none) but not greather than maxwait */
UDNS_API int
dns_timeouts(struct dns_ctx *ctx, int maxwait, time_t now);
/* define timer requesting routine to use */
typedef void dns_utm_fn(struct dns_ctx *ctx, int timeout, void *data);
UDNS_API void
dns_set_tmcbck(struct dns_ctx *ctx, dns_utm_fn *fn, void *data);
/**************************************************************************/
/**************** Making Queries ******************************************/
/* query callback routine */
typedef void dns_query_fn(struct dns_ctx *ctx, void *result, void *data);
/* query parse routine: raw DNS => application structure */
typedef int
dns_parse_fn(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end,
void **res);
enum dns_status {
DNS_E_NOERROR = 0, /* ok, not an error */
DNS_E_TEMPFAIL = -1, /* timeout, SERVFAIL or similar */
DNS_E_PROTOCOL = -2, /* got garbled reply */
DNS_E_NXDOMAIN = -3, /* domain does not exists */
DNS_E_NODATA = -4, /* domain exists but no data of reqd type */
DNS_E_NOMEM = -5, /* out of memory while processing */
DNS_E_BADQUERY = -6 /* the query is malformed */
};
/* submit generic DN query */
UDNS_API struct dns_query *
dns_submit_dn(struct dns_ctx *ctx,
dnscc_t *dn, int qcls, int qtyp, int flags,
dns_parse_fn *parse, dns_query_fn *cbck, void *data);
/* submit generic name query */
UDNS_API struct dns_query *
dns_submit_p(struct dns_ctx *ctx,
const char *name, int qcls, int qtyp, int flags,
dns_parse_fn *parse, dns_query_fn *cbck, void *data);
/* cancel the given async query in progress */
UDNS_API int
dns_cancel(struct dns_ctx *ctx, struct dns_query *q);
/* resolve a generic query, return the answer */
UDNS_API void *
dns_resolve_dn(struct dns_ctx *ctx,
dnscc_t *qdn, int qcls, int qtyp, int flags,
dns_parse_fn *parse);
UDNS_API void *
dns_resolve_p(struct dns_ctx *ctx,
const char *qname, int qcls, int qtyp, int flags,
dns_parse_fn *parse);
UDNS_API void *
dns_resolve(struct dns_ctx *ctx, struct dns_query *q);
/* Specific RR handlers */
#define dns_rr_common(prefix) \
char *prefix##_cname; /* canonical name */ \
char *prefix##_qname; /* original query name */ \
unsigned prefix##_ttl; /* TTL value */ \
int prefix##_nrr /* number of records */
struct dns_rr_null { /* NULL RRset, aka RRset template */
dns_rr_common(dnsn);
};
UDNS_API int
dns_stdrr_size(const struct dns_parse *p);
UDNS_API void *
dns_stdrr_finish(struct dns_rr_null *ret, char *cp, const struct dns_parse *p);
struct dns_rr_a4 { /* the A RRset */
dns_rr_common(dnsa4);
struct in_addr *dnsa4_addr; /* array of addresses, naddr elements */
};
UDNS_API dns_parse_fn dns_parse_a4; /* A RR parsing routine */
typedef void /* A query callback routine */
dns_query_a4_fn(struct dns_ctx *ctx, struct dns_rr_a4 *result, void *data);
/* submit A IN query */
UDNS_API struct dns_query *
dns_submit_a4(struct dns_ctx *ctx, const char *name, int flags,
dns_query_a4_fn *cbck, void *data);
/* resolve A IN query */
UDNS_API struct dns_rr_a4 *
dns_resolve_a4(struct dns_ctx *ctx, const char *name, int flags);
struct dns_rr_a6 { /* the AAAA RRset */
dns_rr_common(dnsa6);
struct in6_addr *dnsa6_addr; /* array of addresses, naddr elements */
};
UDNS_API dns_parse_fn dns_parse_a6; /* A RR parsing routine */
typedef void /* A query callback routine */
dns_query_a6_fn(struct dns_ctx *ctx, struct dns_rr_a6 *result, void *data);
/* submit AAAA IN query */
UDNS_API struct dns_query *
dns_submit_a6(struct dns_ctx *ctx, const char *name, int flags,
dns_query_a6_fn *cbck, void *data);
/* resolve AAAA IN query */
UDNS_API struct dns_rr_a6 *
dns_resolve_a6(struct dns_ctx *ctx, const char *name, int flags);
struct dns_rr_ptr { /* the PTR RRset */
dns_rr_common(dnsptr);
char **dnsptr_ptr; /* array of PTRs */
};
UDNS_API dns_parse_fn dns_parse_ptr; /* PTR RR parsing routine */
typedef void /* PTR query callback */
dns_query_ptr_fn(struct dns_ctx *ctx, struct dns_rr_ptr *result, void *data);
/* submit PTR IN in-addr.arpa query */
UDNS_API struct dns_query *
dns_submit_a4ptr(struct dns_ctx *ctx, const struct in_addr *addr,
dns_query_ptr_fn *cbck, void *data);
/* resolve PTR IN in-addr.arpa query */
UDNS_API struct dns_rr_ptr *
dns_resolve_a4ptr(struct dns_ctx *ctx, const struct in_addr *addr);
/* the same as above, but for ip6.arpa */
UDNS_API struct dns_query *
dns_submit_a6ptr(struct dns_ctx *ctx, const struct in6_addr *addr,
dns_query_ptr_fn *cbck, void *data);
UDNS_API struct dns_rr_ptr *
dns_resolve_a6ptr(struct dns_ctx *ctx, const struct in6_addr *addr);
struct dns_mx { /* single MX RR */
int priority; /* MX priority */
char *name; /* MX name */
};
struct dns_rr_mx { /* the MX RRset */
dns_rr_common(dnsmx);
struct dns_mx *dnsmx_mx; /* array of MXes */
};
UDNS_API dns_parse_fn dns_parse_mx; /* MX RR parsing routine */
typedef void /* MX RR callback */
dns_query_mx_fn(struct dns_ctx *ctx, struct dns_rr_mx *result, void *data);
/* submit MX IN query */
UDNS_API struct dns_query *
dns_submit_mx(struct dns_ctx *ctx, const char *name, int flags,
dns_query_mx_fn *cbck, void *data);
/* resolve MX IN query */
UDNS_API struct dns_rr_mx *
dns_resolve_mx(struct dns_ctx *ctx, const char *name, int flags);
struct dns_txt { /* single TXT record */
int len; /* length of the text */
dnsc_t *txt; /* pointer to text buffer. May contain nulls. */
};
struct dns_rr_txt { /* the TXT RRset */
dns_rr_common(dnstxt);
struct dns_txt *dnstxt_txt; /* array of TXT records */
};
UDNS_API dns_parse_fn dns_parse_txt; /* TXT RR parsing routine */
typedef void /* TXT RR callback */
dns_query_txt_fn(struct dns_ctx *ctx, struct dns_rr_txt *result, void *data);
/* submit TXT query */
UDNS_API struct dns_query *
dns_submit_txt(struct dns_ctx *ctx, const char *name, int qcls, int flags,
dns_query_txt_fn *cbck, void *data);
/* resolve TXT query */
UDNS_API struct dns_rr_txt *
dns_resolve_txt(struct dns_ctx *ctx, const char *name, int qcls, int flags);
struct dns_srv { /* single SRV RR */
int priority; /* SRV priority */
int weight; /* SRV weight */
int port; /* SRV port */
char *name; /* SRV name */
};
struct dns_rr_srv { /* the SRV RRset */
dns_rr_common(dnssrv);
struct dns_srv *dnssrv_srv; /* array of SRVes */
};
UDNS_API dns_parse_fn dns_parse_srv; /* SRV RR parsing routine */
typedef void /* SRV RR callback */
dns_query_srv_fn(struct dns_ctx *ctx, struct dns_rr_srv *result, void *data);
/* submit SRV IN query */
UDNS_API struct dns_query *
dns_submit_srv(struct dns_ctx *ctx,
const char *name, const char *srv, const char *proto,
int flags, dns_query_srv_fn *cbck, void *data);
/* resolve SRV IN query */
UDNS_API struct dns_rr_srv *
dns_resolve_srv(struct dns_ctx *ctx,
const char *name, const char *srv, const char *proto,
int flags);
/* NAPTR (RFC3403) RR type */
struct dns_naptr { /* single NAPTR RR */
int order; /* NAPTR order */
int preference; /* NAPTR preference */
char *flags; /* NAPTR flags */
char *service; /* NAPTR service */
char *regexp; /* NAPTR regexp */
char *replacement; /* NAPTR replacement */
};
struct dns_rr_naptr { /* the NAPTR RRset */
dns_rr_common(dnsnaptr);
struct dns_naptr *dnsnaptr_naptr; /* array of NAPTRes */
};
UDNS_API dns_parse_fn dns_parse_naptr; /* NAPTR RR parsing routine */
typedef void /* NAPTR RR callback */
dns_query_naptr_fn(struct dns_ctx *ctx,
struct dns_rr_naptr *result, void *data);
/* submit NAPTR IN query */
UDNS_API struct dns_query *
dns_submit_naptr(struct dns_ctx *ctx, const char *name, int flags,
dns_query_naptr_fn *cbck, void *data);
/* resolve NAPTR IN query */
UDNS_API struct dns_rr_naptr *
dns_resolve_naptr(struct dns_ctx *ctx, const char *name, int flags);
UDNS_API struct dns_query *
dns_submit_a4dnsbl(struct dns_ctx *ctx,
const struct in_addr *addr, const char *dnsbl,
dns_query_a4_fn *cbck, void *data);
UDNS_API struct dns_query *
dns_submit_a4dnsbl_txt(struct dns_ctx *ctx,
const struct in_addr *addr, const char *dnsbl,
dns_query_txt_fn *cbck, void *data);
UDNS_API struct dns_rr_a4 *
dns_resolve_a4dnsbl(struct dns_ctx *ctx,
const struct in_addr *addr, const char *dnsbl);
UDNS_API struct dns_rr_txt *
dns_resolve_a4dnsbl_txt(struct dns_ctx *ctx,
const struct in_addr *addr, const char *dnsbl);
UDNS_API struct dns_query *
dns_submit_a6dnsbl(struct dns_ctx *ctx,
const struct in6_addr *addr, const char *dnsbl,
dns_query_a4_fn *cbck, void *data);
UDNS_API struct dns_query *
dns_submit_a6dnsbl_txt(struct dns_ctx *ctx,
const struct in6_addr *addr, const char *dnsbl,
dns_query_txt_fn *cbck, void *data);
UDNS_API struct dns_rr_a4 *
dns_resolve_a6dnsbl(struct dns_ctx *ctx,
const struct in6_addr *addr, const char *dnsbl);
UDNS_API struct dns_rr_txt *
dns_resolve_a6dnsbl_txt(struct dns_ctx *ctx,
const struct in6_addr *addr, const char *dnsbl);
UDNS_API struct dns_query *
dns_submit_rhsbl(struct dns_ctx *ctx,
const char *name, const char *rhsbl,
dns_query_a4_fn *cbck, void *data);
UDNS_API struct dns_query *
dns_submit_rhsbl_txt(struct dns_ctx *ctx,
const char *name, const char *rhsbl,
dns_query_txt_fn *cbck, void *data);
UDNS_API struct dns_rr_a4 *
dns_resolve_rhsbl(struct dns_ctx *ctx, const char *name, const char *rhsbl);
UDNS_API struct dns_rr_txt *
dns_resolve_rhsbl_txt(struct dns_ctx *ctx, const char *name, const char *rhsbl);
/**************************************************************************/
/**************** Names, Names ********************************************/
struct dns_nameval {
int val;
const char *name;
};
UDNS_DATA_API extern const struct dns_nameval dns_classtab[];
UDNS_DATA_API extern const struct dns_nameval dns_typetab[];
UDNS_DATA_API extern const struct dns_nameval dns_rcodetab[];
UDNS_API int
dns_findname(const struct dns_nameval *nv, const char *name);
#define dns_findclassname(cls) dns_findname(dns_classtab, (cls))
#define dns_findtypename(type) dns_findname(dns_typetab, (type))
#define dns_findrcodename(rcode) dns_findname(dns_rcodetab, (rcode))
UDNS_API const char *dns_classname(enum dns_class cls);
UDNS_API const char *dns_typename(enum dns_type type);
UDNS_API const char *dns_rcodename(enum dns_rcode rcode);
const char *_dns_format_code(char *buf, const char *prefix, int code);
UDNS_API const char *dns_strerror(int errnum);
/* simple pseudo-random number generator, code by Bob Jenkins */
struct udns_jranctx { /* the context */
unsigned a, b, c, d;
};
/* initialize the RNG with a given seed */
UDNS_API void
udns_jraninit(struct udns_jranctx *x, unsigned seed);
/* return next random number. 32bits on most platforms so far. */
UDNS_API unsigned
udns_jranval(struct udns_jranctx *x);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* include guard */

View File

@ -0,0 +1,50 @@
/* udns_XtoX.c
udns_ntop() and udns_pton() routines, which are either
- wrappers for inet_ntop() and inet_pton() or
- reimplementations of those routines.
Copyright (C) 2005 Michael Tokarev <mjt@corpit.ru>
This file is part of UDNS library, an async DNS stub resolver.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library, in file named COPYING.LGPL; if not,
write to the Free Software Foundation, Inc., 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "udns.h"
#ifdef HAVE_INET_PTON_NTOP
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
const char *dns_ntop(int af, const void *src, char *dst, int size) {
return inet_ntop(af, src, dst, size);
}
int dns_pton(int af, const char *src, void *dst) {
return inet_pton(af, src, dst);
}
#else
#define inet_XtoX_prefix udns_
#include "inet_XtoX.c"
#endif

View File

@ -0,0 +1,160 @@
/* udns_bl.c
DNSBL stuff
Copyright (C) 2005 Michael Tokarev <mjt@corpit.ru>
This file is part of UDNS library, an async DNS stub resolver.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library, in file named COPYING.LGPL; if not,
write to the Free Software Foundation, Inc., 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA
*/
#include "udns.h"
#ifndef NULL
# define NULL 0
#endif
struct dns_query *
dns_submit_a4dnsbl(struct dns_ctx *ctx,
const struct in_addr *addr, const char *dnsbl,
dns_query_a4_fn *cbck, void *data) {
dnsc_t dn[DNS_MAXDN];
if (dns_a4ptodn(addr, dnsbl, dn, sizeof(dn)) <= 0) {
dns_setstatus(ctx, DNS_E_BADQUERY);
return NULL;
}
return
dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_A, DNS_NOSRCH,
dns_parse_a4, (dns_query_fn*)cbck, data);
}
struct dns_query *
dns_submit_a4dnsbl_txt(struct dns_ctx *ctx,
const struct in_addr *addr, const char *dnsbl,
dns_query_txt_fn *cbck, void *data) {
dnsc_t dn[DNS_MAXDN];
if (dns_a4ptodn(addr, dnsbl, dn, sizeof(dn)) <= 0) {
dns_setstatus(ctx, DNS_E_BADQUERY);
return NULL;
}
return
dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_TXT, DNS_NOSRCH,
dns_parse_txt, (dns_query_fn*)cbck, data);
}
struct dns_rr_a4 *
dns_resolve_a4dnsbl(struct dns_ctx *ctx,
const struct in_addr *addr, const char *dnsbl) {
return (struct dns_rr_a4 *)
dns_resolve(ctx, dns_submit_a4dnsbl(ctx, addr, dnsbl, 0, 0));
}
struct dns_rr_txt *
dns_resolve_a4dnsbl_txt(struct dns_ctx *ctx,
const struct in_addr *addr, const char *dnsbl) {
return (struct dns_rr_txt *)
dns_resolve(ctx, dns_submit_a4dnsbl_txt(ctx, addr, dnsbl, 0, 0));
}
struct dns_query *
dns_submit_a6dnsbl(struct dns_ctx *ctx,
const struct in6_addr *addr, const char *dnsbl,
dns_query_a4_fn *cbck, void *data) {
dnsc_t dn[DNS_MAXDN];
if (dns_a6ptodn(addr, dnsbl, dn, sizeof(dn)) <= 0) {
dns_setstatus(ctx, DNS_E_BADQUERY);
return NULL;
}
return
dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_A, DNS_NOSRCH,
dns_parse_a4, (dns_query_fn*)cbck, data);
}
struct dns_query *
dns_submit_a6dnsbl_txt(struct dns_ctx *ctx,
const struct in6_addr *addr, const char *dnsbl,
dns_query_txt_fn *cbck, void *data) {
dnsc_t dn[DNS_MAXDN];
if (dns_a6ptodn(addr, dnsbl, dn, sizeof(dn)) <= 0) {
dns_setstatus(ctx, DNS_E_BADQUERY);
return NULL;
}
return
dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_TXT, DNS_NOSRCH,
dns_parse_txt, (dns_query_fn*)cbck, data);
}
struct dns_rr_a4 *
dns_resolve_a6dnsbl(struct dns_ctx *ctx,
const struct in6_addr *addr, const char *dnsbl) {
return (struct dns_rr_a4 *)
dns_resolve(ctx, dns_submit_a6dnsbl(ctx, addr, dnsbl, 0, 0));
}
struct dns_rr_txt *
dns_resolve_a6dnsbl_txt(struct dns_ctx *ctx,
const struct in6_addr *addr, const char *dnsbl) {
return (struct dns_rr_txt *)
dns_resolve(ctx, dns_submit_a6dnsbl_txt(ctx, addr, dnsbl, 0, 0));
}
static int
dns_rhsbltodn(const char *name, const char *rhsbl, dnsc_t dn[DNS_MAXDN])
{
int l = dns_sptodn(name, dn, DNS_MAXDN);
if (l <= 0) return 0;
l = dns_sptodn(rhsbl, dn+l-1, DNS_MAXDN-l+1);
if (l <= 0) return 0;
return 1;
}
struct dns_query *
dns_submit_rhsbl(struct dns_ctx *ctx, const char *name, const char *rhsbl,
dns_query_a4_fn *cbck, void *data) {
dnsc_t dn[DNS_MAXDN];
if (!dns_rhsbltodn(name, rhsbl, dn)) {
dns_setstatus(ctx, DNS_E_BADQUERY);
return NULL;
}
return
dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_A, DNS_NOSRCH,
dns_parse_a4, (dns_query_fn*)cbck, data);
}
struct dns_query *
dns_submit_rhsbl_txt(struct dns_ctx *ctx, const char *name, const char *rhsbl,
dns_query_txt_fn *cbck, void *data) {
dnsc_t dn[DNS_MAXDN];
if (!dns_rhsbltodn(name, rhsbl, dn)) {
dns_setstatus(ctx, DNS_E_BADQUERY);
return NULL;
}
return
dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_TXT, DNS_NOSRCH,
dns_parse_txt, (dns_query_fn*)cbck, data);
}
struct dns_rr_a4 *
dns_resolve_rhsbl(struct dns_ctx *ctx, const char *name, const char *rhsbl) {
return (struct dns_rr_a4*)
dns_resolve(ctx, dns_submit_rhsbl(ctx, name, rhsbl, 0, 0));
}
struct dns_rr_txt *
dns_resolve_rhsbl_txt(struct dns_ctx *ctx, const char *name, const char *rhsbl)
{
return (struct dns_rr_txt*)
dns_resolve(ctx, dns_submit_rhsbl_txt(ctx, name, rhsbl, 0, 0));
}

View File

@ -0,0 +1,199 @@
/* Automatically generated. */
#include "udns.h"
const struct dns_nameval dns_typetab[] = {
{DNS_T_INVALID,"INVALID"},
{DNS_T_A,"A"},
{DNS_T_NS,"NS"},
{DNS_T_MD,"MD"},
{DNS_T_MF,"MF"},
{DNS_T_CNAME,"CNAME"},
{DNS_T_SOA,"SOA"},
{DNS_T_MB,"MB"},
{DNS_T_MG,"MG"},
{DNS_T_MR,"MR"},
{DNS_T_NULL,"NULL"},
{DNS_T_WKS,"WKS"},
{DNS_T_PTR,"PTR"},
{DNS_T_HINFO,"HINFO"},
{DNS_T_MINFO,"MINFO"},
{DNS_T_MX,"MX"},
{DNS_T_TXT,"TXT"},
{DNS_T_RP,"RP"},
{DNS_T_AFSDB,"AFSDB"},
{DNS_T_X25,"X25"},
{DNS_T_ISDN,"ISDN"},
{DNS_T_RT,"RT"},
{DNS_T_NSAP,"NSAP"},
{DNS_T_NSAP_PTR,"NSAP_PTR"},
{DNS_T_SIG,"SIG"},
{DNS_T_KEY,"KEY"},
{DNS_T_PX,"PX"},
{DNS_T_GPOS,"GPOS"},
{DNS_T_AAAA,"AAAA"},
{DNS_T_LOC,"LOC"},
{DNS_T_NXT,"NXT"},
{DNS_T_EID,"EID"},
{DNS_T_NIMLOC,"NIMLOC"},
{DNS_T_SRV,"SRV"},
{DNS_T_ATMA,"ATMA"},
{DNS_T_NAPTR,"NAPTR"},
{DNS_T_KX,"KX"},
{DNS_T_CERT,"CERT"},
{DNS_T_A6,"A6"},
{DNS_T_DNAME,"DNAME"},
{DNS_T_SINK,"SINK"},
{DNS_T_OPT,"OPT"},
{DNS_T_DS,"DS"},
{DNS_T_SSHFP,"SSHFP"},
{DNS_T_IPSECKEY,"IPSECKEY"},
{DNS_T_RRSIG,"RRSIG"},
{DNS_T_NSEC,"NSEC"},
{DNS_T_DNSKEY,"DNSKEY"},
{DNS_T_DHCID,"DHCID"},
{DNS_T_NSEC3,"NSEC3"},
{DNS_T_NSEC3PARAMS,"NSEC3PARAMS"},
{DNS_T_TALINK,"TALINK"},
{DNS_T_SPF,"SPF"},
{DNS_T_UINFO,"UINFO"},
{DNS_T_UID,"UID"},
{DNS_T_GID,"GID"},
{DNS_T_UNSPEC,"UNSPEC"},
{DNS_T_TSIG,"TSIG"},
{DNS_T_IXFR,"IXFR"},
{DNS_T_AXFR,"AXFR"},
{DNS_T_MAILB,"MAILB"},
{DNS_T_MAILA,"MAILA"},
{DNS_T_ANY,"ANY"},
{DNS_T_ZXFR,"ZXFR"},
{DNS_T_DLV,"DLV"},
{DNS_T_MAX,"MAX"},
{0,0}};
const char *dns_typename(enum dns_type code) {
static char nm[20];
switch(code) {
case DNS_T_INVALID: return dns_typetab[0].name;
case DNS_T_A: return dns_typetab[1].name;
case DNS_T_NS: return dns_typetab[2].name;
case DNS_T_MD: return dns_typetab[3].name;
case DNS_T_MF: return dns_typetab[4].name;
case DNS_T_CNAME: return dns_typetab[5].name;
case DNS_T_SOA: return dns_typetab[6].name;
case DNS_T_MB: return dns_typetab[7].name;
case DNS_T_MG: return dns_typetab[8].name;
case DNS_T_MR: return dns_typetab[9].name;
case DNS_T_NULL: return dns_typetab[10].name;
case DNS_T_WKS: return dns_typetab[11].name;
case DNS_T_PTR: return dns_typetab[12].name;
case DNS_T_HINFO: return dns_typetab[13].name;
case DNS_T_MINFO: return dns_typetab[14].name;
case DNS_T_MX: return dns_typetab[15].name;
case DNS_T_TXT: return dns_typetab[16].name;
case DNS_T_RP: return dns_typetab[17].name;
case DNS_T_AFSDB: return dns_typetab[18].name;
case DNS_T_X25: return dns_typetab[19].name;
case DNS_T_ISDN: return dns_typetab[20].name;
case DNS_T_RT: return dns_typetab[21].name;
case DNS_T_NSAP: return dns_typetab[22].name;
case DNS_T_NSAP_PTR: return dns_typetab[23].name;
case DNS_T_SIG: return dns_typetab[24].name;
case DNS_T_KEY: return dns_typetab[25].name;
case DNS_T_PX: return dns_typetab[26].name;
case DNS_T_GPOS: return dns_typetab[27].name;
case DNS_T_AAAA: return dns_typetab[28].name;
case DNS_T_LOC: return dns_typetab[29].name;
case DNS_T_NXT: return dns_typetab[30].name;
case DNS_T_EID: return dns_typetab[31].name;
case DNS_T_NIMLOC: return dns_typetab[32].name;
case DNS_T_SRV: return dns_typetab[33].name;
case DNS_T_ATMA: return dns_typetab[34].name;
case DNS_T_NAPTR: return dns_typetab[35].name;
case DNS_T_KX: return dns_typetab[36].name;
case DNS_T_CERT: return dns_typetab[37].name;
case DNS_T_A6: return dns_typetab[38].name;
case DNS_T_DNAME: return dns_typetab[39].name;
case DNS_T_SINK: return dns_typetab[40].name;
case DNS_T_OPT: return dns_typetab[41].name;
case DNS_T_DS: return dns_typetab[42].name;
case DNS_T_SSHFP: return dns_typetab[43].name;
case DNS_T_IPSECKEY: return dns_typetab[44].name;
case DNS_T_RRSIG: return dns_typetab[45].name;
case DNS_T_NSEC: return dns_typetab[46].name;
case DNS_T_DNSKEY: return dns_typetab[47].name;
case DNS_T_DHCID: return dns_typetab[48].name;
case DNS_T_NSEC3: return dns_typetab[49].name;
case DNS_T_NSEC3PARAMS: return dns_typetab[50].name;
case DNS_T_TALINK: return dns_typetab[51].name;
case DNS_T_SPF: return dns_typetab[52].name;
case DNS_T_UINFO: return dns_typetab[53].name;
case DNS_T_UID: return dns_typetab[54].name;
case DNS_T_GID: return dns_typetab[55].name;
case DNS_T_UNSPEC: return dns_typetab[56].name;
case DNS_T_TSIG: return dns_typetab[57].name;
case DNS_T_IXFR: return dns_typetab[58].name;
case DNS_T_AXFR: return dns_typetab[59].name;
case DNS_T_MAILB: return dns_typetab[60].name;
case DNS_T_MAILA: return dns_typetab[61].name;
case DNS_T_ANY: return dns_typetab[62].name;
case DNS_T_ZXFR: return dns_typetab[63].name;
case DNS_T_DLV: return dns_typetab[64].name;
case DNS_T_MAX: return dns_typetab[65].name;
}
return _dns_format_code(nm,"type",code);
}
const struct dns_nameval dns_classtab[] = {
{DNS_C_INVALID,"INVALID"},
{DNS_C_IN,"IN"},
{DNS_C_CH,"CH"},
{DNS_C_HS,"HS"},
{DNS_C_ANY,"ANY"},
{0,0}};
const char *dns_classname(enum dns_class code) {
static char nm[20];
switch(code) {
case DNS_C_INVALID: return dns_classtab[0].name;
case DNS_C_IN: return dns_classtab[1].name;
case DNS_C_CH: return dns_classtab[2].name;
case DNS_C_HS: return dns_classtab[3].name;
case DNS_C_ANY: return dns_classtab[4].name;
}
return _dns_format_code(nm,"class",code);
}
const struct dns_nameval dns_rcodetab[] = {
{DNS_R_NOERROR,"NOERROR"},
{DNS_R_FORMERR,"FORMERR"},
{DNS_R_SERVFAIL,"SERVFAIL"},
{DNS_R_NXDOMAIN,"NXDOMAIN"},
{DNS_R_NOTIMPL,"NOTIMPL"},
{DNS_R_REFUSED,"REFUSED"},
{DNS_R_YXDOMAIN,"YXDOMAIN"},
{DNS_R_YXRRSET,"YXRRSET"},
{DNS_R_NXRRSET,"NXRRSET"},
{DNS_R_NOTAUTH,"NOTAUTH"},
{DNS_R_NOTZONE,"NOTZONE"},
{DNS_R_BADSIG,"BADSIG"},
{DNS_R_BADKEY,"BADKEY"},
{DNS_R_BADTIME,"BADTIME"},
{0,0}};
const char *dns_rcodename(enum dns_rcode code) {
static char nm[20];
switch(code) {
case DNS_R_NOERROR: return dns_rcodetab[0].name;
case DNS_R_FORMERR: return dns_rcodetab[1].name;
case DNS_R_SERVFAIL: return dns_rcodetab[2].name;
case DNS_R_NXDOMAIN: return dns_rcodetab[3].name;
case DNS_R_NOTIMPL: return dns_rcodetab[4].name;
case DNS_R_REFUSED: return dns_rcodetab[5].name;
case DNS_R_YXDOMAIN: return dns_rcodetab[6].name;
case DNS_R_YXRRSET: return dns_rcodetab[7].name;
case DNS_R_NXRRSET: return dns_rcodetab[8].name;
case DNS_R_NOTAUTH: return dns_rcodetab[9].name;
case DNS_R_NOTZONE: return dns_rcodetab[10].name;
case DNS_R_BADSIG: return dns_rcodetab[11].name;
case DNS_R_BADKEY: return dns_rcodetab[12].name;
case DNS_R_BADTIME: return dns_rcodetab[13].name;
}
return _dns_format_code(nm,"rcode",code);
}

View File

@ -0,0 +1,379 @@
/* udns_dn.c
domain names manipulation routines
Copyright (C) 2005 Michael Tokarev <mjt@corpit.ru>
This file is part of UDNS library, an async DNS stub resolver.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library, in file named COPYING.LGPL; if not,
write to the Free Software Foundation, Inc., 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA
*/
#include <string.h>
#include "udns.h"
unsigned dns_dnlen(dnscc_t *dn) {
register dnscc_t *d = dn;
while(*d)
d += 1 + *d;
return (unsigned)(d - dn) + 1;
}
unsigned dns_dnlabels(register dnscc_t *dn) {
register unsigned l = 0;
while(*dn)
++l, dn += 1 + *dn;
return l;
}
unsigned dns_dnequal(register dnscc_t *dn1, register dnscc_t *dn2) {
register unsigned c;
dnscc_t *dn = dn1;
for(;;) {
if ((c = *dn1++) != *dn2++)
return 0;
if (!c)
return (unsigned)(dn1 - dn);
while(c--) {
if (DNS_DNLC(*dn1) != DNS_DNLC(*dn2))
return 0;
++dn1; ++dn2;
}
}
}
unsigned
dns_dntodn(dnscc_t *sdn, dnsc_t *ddn, unsigned ddnsiz) {
unsigned sdnlen = dns_dnlen(sdn);
if (ddnsiz < sdnlen)
return 0;
memcpy(ddn, sdn, sdnlen);
return sdnlen;
}
int
dns_ptodn(const char *name, unsigned namelen,
dnsc_t *dn, unsigned dnsiz, int *isabs)
{
dnsc_t *dp; /* current position in dn (len byte first) */
dnsc_t *const de /* end of dn: last byte that can be filled up */
= dn + (dnsiz >= DNS_MAXDN ? DNS_MAXDN : dnsiz) - 1;
dnscc_t *np = (dnscc_t *)name;
dnscc_t *ne = np + (namelen ? namelen : strlen((char*)np));
dnsc_t *llab; /* start of last label (llab[-1] will be length) */
unsigned c; /* next input character, or length of last label */
if (!dnsiz)
return 0;
dp = llab = dn + 1;
while(np < ne) {
if (*np == '.') { /* label delimiter */
c = dp - llab; /* length of the label */
if (!c) { /* empty label */
if (np == (dnscc_t *)name && np + 1 == ne) {
/* special case for root dn, aka `.' */
++np;
break;
}
return -1; /* zero label */
}
if (c > DNS_MAXLABEL)
return -1; /* label too long */
llab[-1] = (dnsc_t)c; /* update len of last label */
llab = ++dp; /* start new label, llab[-1] will be len of it */
++np;
continue;
}
/* check whenever we may put out one more byte */
if (dp >= de) /* too long? */
return dnsiz >= DNS_MAXDN ? -1 : 0;
if (*np != '\\') { /* non-escape, simple case */
*dp++ = *np++;
continue;
}
/* handle \-style escape */
/* note that traditionally, domain names (gethostbyname etc)
* used decimal \dd notation, not octal \ooo (RFC1035), so
* we're following this tradition here.
*/
if (++np == ne)
return -1; /* bad escape */
else if (*np >= '0' && *np <= '9') { /* decimal number */
/* we allow not only exactly 3 digits as per RFC1035,
* but also 2 or 1, for better usability. */
c = *np++ - '0';
if (np < ne && *np >= '0' && *np <= '9') { /* 2digits */
c = c * 10 + *np++ - '0';
if (np < ne && *np >= '0' && *np <= '9') {
c = c * 10 + *np++ - '0';
if (c > 255)
return -1; /* bad escape */
}
}
}
else
c = *np++;
*dp++ = (dnsc_t)c; /* place next out byte */
}
if ((c = dp - llab) > DNS_MAXLABEL)
return -1; /* label too long */
if ((llab[-1] = (dnsc_t)c) != 0) {
*dp++ = 0;
if (isabs)
*isabs = 0;
}
else if (isabs)
*isabs = 1;
return dp - dn;
}
dnscc_t dns_inaddr_arpa_dn[14] = "\07in-addr\04arpa";
dnsc_t *
dns_a4todn_(const struct in_addr *addr, dnsc_t *dn, dnsc_t *dne) {
const unsigned char *s = ((const unsigned char *)addr) + 4;
while(s > (const unsigned char *)addr) {
unsigned n = *--s;
dnsc_t *p = dn + 1;
if (n > 99) {
if (p + 2 > dne) return 0;
*p++ = n / 100 + '0';
*p++ = (n % 100 / 10) + '0';
*p = n % 10 + '0';
}
else if (n > 9) {
if (p + 1 > dne) return 0;
*p++ = n / 10 + '0';
*p = n % 10 + '0';
}
else {
if (p > dne) return 0;
*p = n + '0';
}
*dn = p - dn;
dn = p + 1;
}
return dn;
}
int dns_a4todn(const struct in_addr *addr, dnscc_t *tdn,
dnsc_t *dn, unsigned dnsiz) {
dnsc_t *dne = dn + (dnsiz > DNS_MAXDN ? DNS_MAXDN : dnsiz);
dnsc_t *p;
unsigned l;
p = dns_a4todn_(addr, dn, dne);
if (!p) return 0;
if (!tdn)
tdn = dns_inaddr_arpa_dn;
l = dns_dnlen(tdn);
if (p + l > dne) return dnsiz >= DNS_MAXDN ? -1 : 0;
memcpy(p, tdn, l);
return (p + l) - dn;
}
int dns_a4ptodn(const struct in_addr *addr, const char *tname,
dnsc_t *dn, unsigned dnsiz) {
dnsc_t *p;
int r;
if (!tname)
return dns_a4todn(addr, NULL, dn, dnsiz);
p = dns_a4todn_(addr, dn, dn + dnsiz);
if (!p) return 0;
r = dns_sptodn(tname, p, dnsiz - (p - dn));
return r != 0 ? r : dnsiz >= DNS_MAXDN ? -1 : 0;
}
dnscc_t dns_ip6_arpa_dn[10] = "\03ip6\04arpa";
dnsc_t *
dns_a6todn_(const struct in6_addr *addr, dnsc_t *dn, dnsc_t *dne) {
const unsigned char *s = ((const unsigned char *)addr) + 16;
if (dn + 64 > dne) return 0;
while(s > (const unsigned char *)addr) {
unsigned n = *--s & 0x0f;
*dn++ = 1;
*dn++ = n > 9 ? n + 'a' - 10 : n + '0';
*dn++ = 1;
n = *s >> 4;
*dn++ = n > 9 ? n + 'a' - 10 : n + '0';
}
return dn;
}
int dns_a6todn(const struct in6_addr *addr, dnscc_t *tdn,
dnsc_t *dn, unsigned dnsiz) {
dnsc_t *dne = dn + (dnsiz > DNS_MAXDN ? DNS_MAXDN : dnsiz);
dnsc_t *p;
unsigned l;
p = dns_a6todn_(addr, dn, dne);
if (!p) return 0;
if (!tdn)
tdn = dns_ip6_arpa_dn;
l = dns_dnlen(tdn);
if (p + l > dne) return dnsiz >= DNS_MAXDN ? -1 : 0;
memcpy(p, tdn, l);
return (p + l) - dn;
}
int dns_a6ptodn(const struct in6_addr *addr, const char *tname,
dnsc_t *dn, unsigned dnsiz) {
dnsc_t *p;
int r;
if (!tname)
return dns_a6todn(addr, NULL, dn, dnsiz);
p = dns_a6todn_(addr, dn, dn + dnsiz);
if (!p) return 0;
r = dns_sptodn(tname, p, dnsiz - (p - dn));
return r != 0 ? r : dnsiz >= DNS_MAXDN ? -1 : 0;
}
/* return size of buffer required to convert the dn into asciiz string.
* Keep in sync with dns_dntop() below.
*/
unsigned dns_dntop_size(dnscc_t *dn) {
unsigned size = 0; /* the size reqd */
dnscc_t *le; /* label end */
while(*dn) {
/* *dn is the length of the next label, non-zero */
if (size)
++size; /* for the dot */
le = dn + *dn + 1;
++dn;
do {
switch(*dn) {
case '.':
case '\\':
/* Special modifiers in zone files. */
case '"':
case ';':
case '@':
case '$':
size += 2;
break;
default:
if (*dn <= 0x20 || *dn >= 0x7f)
/* \ddd decimal notation */
size += 4;
else
size += 1;
}
} while(++dn < le);
}
size += 1; /* zero byte at the end - string terminator */
return size > DNS_MAXNAME ? 0 : size;
}
/* Convert the dn into asciiz string.
* Keep in sync with dns_dntop_size() above.
*/
int dns_dntop(dnscc_t *dn, char *name, unsigned namesiz) {
char *np = name; /* current name ptr */
char *const ne = name + namesiz; /* end of name */
dnscc_t *le; /* label end */
while(*dn) {
/* *dn is the length of the next label, non-zero */
if (np != name) {
if (np >= ne) goto toolong;
*np++ = '.';
}
le = dn + *dn + 1;
++dn;
do {
switch(*dn) {
case '.':
case '\\':
/* Special modifiers in zone files. */
case '"':
case ';':
case '@':
case '$':
if (np + 2 > ne) goto toolong;
*np++ = '\\';
*np++ = *dn;
break;
default:
if (*dn <= 0x20 || *dn >= 0x7f) {
/* \ddd decimal notation */
if (np + 4 >= ne) goto toolong;
*np++ = '\\';
*np++ = '0' + (*dn / 100);
*np++ = '0' + ((*dn % 100) / 10);
*np++ = '0' + (*dn % 10);
}
else {
if (np >= ne) goto toolong;
*np++ = *dn;
}
}
} while(++dn < le);
}
if (np >= ne) goto toolong;
*np++ = '\0';
return np - name;
toolong:
return namesiz >= DNS_MAXNAME ? -1 : 0;
}
#ifdef TEST
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
int i;
int sz;
dnsc_t dn[DNS_MAXDN+10];
dnsc_t *dl, *dp;
int isabs;
sz = (argc > 1) ? atoi(argv[1]) : 0;
for(i = 2; i < argc; ++i) {
int r = dns_ptodn(argv[i], 0, dn, sz, &isabs);
printf("%s: ", argv[i]);
if (r < 0) printf("error\n");
else if (!r) printf("buffer too small\n");
else {
printf("len=%d dnlen=%d size=%d name:",
r, dns_dnlen(dn), dns_dntop_size(dn));
dl = dn;
while(*dl) {
printf(" %d=", *dl);
dp = dl + 1;
dl = dp + *dl;
while(dp < dl) {
if (*dp <= ' ' || *dp >= 0x7f)
printf("\\%03d", *dp);
else if (*dp == '.' || *dp == '\\')
printf("\\%c", *dp);
else
putchar(*dp);
++dp;
}
}
if (isabs) putchar('.');
putchar('\n');
}
}
return 0;
}
#endif /* TEST */

View File

@ -0,0 +1,30 @@
/* udns_dntosp.c
dns_dntosp() = convert DN to asciiz string using static buffer
Copyright (C) 2005 Michael Tokarev <mjt@corpit.ru>
This file is part of UDNS library, an async DNS stub resolver.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library, in file named COPYING.LGPL; if not,
write to the Free Software Foundation, Inc., 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA
*/
#include "udns.h"
static char name[DNS_MAXNAME];
const char *dns_dntosp(dnscc_t *dn) {
return dns_dntop(dn, name, sizeof(name)) > 0 ? name : 0;
}

View File

@ -0,0 +1,231 @@
/* udns_init.c
resolver initialisation stuff
Copyright (C) 2006 Michael Tokarev <mjt@corpit.ru>
This file is part of UDNS library, an async DNS stub resolver.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library, in file named COPYING.LGPL; if not,
write to the Free Software Foundation, Inc., 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef WINDOWS
# include <winsock2.h> /* includes <windows.h> */
# include <iphlpapi.h> /* for dns server addresses etc */
#else
# include <sys/types.h>
# include <unistd.h>
# include <fcntl.h>
#endif /* !WINDOWS */
#include <stdlib.h>
#include <string.h>
#include "udns.h"
#define ISSPACE(x) (x == ' ' || x == '\t' || x == '\r' || x == '\n')
static const char space[] = " \t\r\n";
static void dns_set_serv_internal(struct dns_ctx *ctx, char *serv) {
dns_add_serv(ctx, NULL);
for(serv = strtok(serv, space); serv; serv = strtok(NULL, space))
dns_add_serv(ctx, serv);
}
static void dns_set_srch_internal(struct dns_ctx *ctx, char *srch) {
dns_add_srch(ctx, NULL);
for(srch = strtok(srch, space); srch; srch = strtok(NULL, space))
dns_add_srch(ctx, srch);
}
#ifdef WINDOWS
#ifndef NO_IPHLPAPI
/* Apparently, some systems does not have proper headers for IPHLPAIP to work.
* The best is to upgrade headers, but here's another, ugly workaround for
* this: compile with -DNO_IPHLPAPI.
*/
typedef DWORD (WINAPI *GetAdaptersAddressesFunc)(
ULONG Family, DWORD Flags, PVOID Reserved,
PIP_ADAPTER_ADDRESSES pAdapterAddresses,
PULONG pOutBufLen);
static int dns_initns_iphlpapi(struct dns_ctx *ctx) {
HANDLE h_iphlpapi;
GetAdaptersAddressesFunc pfnGetAdAddrs;
PIP_ADAPTER_ADDRESSES pAddr, pAddrBuf;
PIP_ADAPTER_DNS_SERVER_ADDRESS pDnsAddr;
ULONG ulOutBufLen;
DWORD dwRetVal;
int ret = -1;
h_iphlpapi = LoadLibrary("iphlpapi.dll");
if (!h_iphlpapi)
return -1;
pfnGetAdAddrs = (GetAdaptersAddressesFunc)
GetProcAddress(h_iphlpapi, "GetAdaptersAddresses");
if (!pfnGetAdAddrs) goto freelib;
ulOutBufLen = 0;
dwRetVal = pfnGetAdAddrs(AF_UNSPEC, 0, NULL, NULL, &ulOutBufLen);
if (dwRetVal != ERROR_BUFFER_OVERFLOW) goto freelib;
pAddrBuf = malloc(ulOutBufLen);
if (!pAddrBuf) goto freelib;
dwRetVal = pfnGetAdAddrs(AF_UNSPEC, 0, NULL, pAddrBuf, &ulOutBufLen);
if (dwRetVal != ERROR_SUCCESS) goto freemem;
for (pAddr = pAddrBuf; pAddr; pAddr = pAddr->Next)
for (pDnsAddr = pAddr->FirstDnsServerAddress;
pDnsAddr;
pDnsAddr = pDnsAddr->Next)
dns_add_serv_s(ctx, pDnsAddr->Address.lpSockaddr);
ret = 0;
freemem:
free(pAddrBuf);
freelib:
FreeLibrary(h_iphlpapi);
return ret;
}
#else /* NO_IPHLPAPI */
#define dns_initns_iphlpapi(ctx) (-1)
#endif /* NO_IPHLPAPI */
static int dns_initns_registry(struct dns_ctx *ctx) {
LONG res;
HKEY hk;
DWORD type = REG_EXPAND_SZ | REG_SZ;
DWORD len;
char valBuf[1024];
#define REGKEY_WINNT "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"
#define REGKEY_WIN9x "SYSTEM\\CurrentControlSet\\Services\\VxD\\MSTCP"
res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY_WINNT, 0, KEY_QUERY_VALUE, &hk);
if (res != ERROR_SUCCESS)
res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY_WIN9x,
0, KEY_QUERY_VALUE, &hk);
if (res != ERROR_SUCCESS)
return -1;
len = sizeof(valBuf) - 1;
res = RegQueryValueEx(hk, "NameServer", NULL, &type, (BYTE*)valBuf, &len);
if (res != ERROR_SUCCESS || !len || !valBuf[0]) {
len = sizeof(valBuf) - 1;
res = RegQueryValueEx(hk, "DhcpNameServer", NULL, &type,
(BYTE*)valBuf, &len);
}
RegCloseKey(hk);
if (res != ERROR_SUCCESS || !len || !valBuf[0])
return -1;
valBuf[len] = '\0';
/* nameservers are stored as a whitespace-seperate list:
* "192.168.1.1 123.21.32.12" */
dns_set_serv_internal(ctx, valBuf);
return 0;
}
#else /* !WINDOWS */
static int dns_init_resolvconf(struct dns_ctx *ctx) {
char *v;
char buf[2049]; /* this buffer is used to hold /etc/resolv.conf */
int has_srch = 0;
/* read resolv.conf... */
{ int fd = open("/etc/resolv.conf", O_RDONLY);
if (fd >= 0) {
int l = read(fd, buf, sizeof(buf) - 1);
close(fd);
buf[l < 0 ? 0 : l] = '\0';
}
else
buf[0] = '\0';
}
if (buf[0]) { /* ...and parse it */
char *line, *nextline;
line = buf;
do {
nextline = strchr(line, '\n');
if (nextline) *nextline++ = '\0';
v = line;
while(*v && !ISSPACE(*v)) ++v;
if (!*v) continue;
*v++ = '\0';
while(ISSPACE(*v)) ++v;
if (!*v) continue;
if (strcmp(line, "domain") == 0) {
dns_set_srch_internal(ctx, strtok(v, space));
has_srch = 1;
}
else if (strcmp(line, "search") == 0) {
dns_set_srch_internal(ctx, v);
has_srch = 1;
}
else if (strcmp(line, "nameserver") == 0)
dns_add_serv(ctx, strtok(v, space));
else if (strcmp(line, "options") == 0)
dns_set_opts(ctx, v);
} while((line = nextline) != NULL);
}
buf[sizeof(buf)-1] = '\0';
/* get list of nameservers from env. vars. */
if ((v = getenv("NSCACHEIP")) != NULL ||
(v = getenv("NAMESERVERS")) != NULL) {
strncpy(buf, v, sizeof(buf) - 1);
dns_set_serv_internal(ctx, buf);
}
/* if $LOCALDOMAIN is set, use it for search list */
if ((v = getenv("LOCALDOMAIN")) != NULL) {
strncpy(buf, v, sizeof(buf) - 1);
dns_set_srch_internal(ctx, buf);
has_srch = 1;
}
if ((v = getenv("RES_OPTIONS")) != NULL)
dns_set_opts(ctx, v);
/* if still no search list, use local domain name */
if (has_srch &&
gethostname(buf, sizeof(buf) - 1) == 0 &&
(v = strchr(buf, '.')) != NULL &&
*++v != '\0')
dns_add_srch(ctx, v);
return 0;
}
#endif /* !WINDOWS */
int dns_init(struct dns_ctx *ctx, int do_open) {
if (!ctx)
ctx = &dns_defctx;
dns_reset(ctx);
#ifdef WINDOWS
if (dns_initns_iphlpapi(ctx) != 0)
dns_initns_registry(ctx);
/*XXX WINDOWS: probably good to get default domain and search list too...
* And options. Something is in registry. */
/*XXX WINDOWS: maybe environment variables are also useful? */
#else
dns_init_resolvconf(ctx);
#endif
return do_open ? dns_open(ctx) : 0;
}

View File

@ -0,0 +1,52 @@
/* udns_jran.c: small non-cryptographic random number generator
* taken from http://burtleburtle.net/bob/rand/smallprng.html
* by Bob Jenkins, Public domain.
*/
#include "udns.h"
#define rot32(x,k) (((x) << (k)) | ((x) >> (32-(k))))
#define rot64(x,k) (((x) << (k)) | ((x) >> (64-(k))))
#define tr32(x) ((x)&0xffffffffu)
unsigned udns_jranval(struct udns_jranctx *x) {
/* This routine can be made to work with either 32 or 64bit words -
* if JRAN_32_64 is defined when compiling the file.
* We use if() instead of #if since there's no good
* portable way to check sizeof() in preprocessor without
* introducing some ugly configure-time checks.
* Most compilers will optimize the wrong branches away anyway.
* By default it assumes 32bit integers
*/
#ifdef JRAN_32_64
if (sizeof(unsigned) == 4) {
#endif
unsigned e = tr32(x->a - rot32(x->b, 27));
x->a = tr32(x->b ^ rot32(x->c, 17));
x->b = tr32(x->c + x->d);
x->c = tr32(x->d + e);
x->d = tr32(e + x->a);
#ifdef JRAN_32_64
}
else if (sizeof(unsigned) == 8) { /* assuming it's 64bits */
unsigned e = x->a - rot64(x->b, 7);
x->a = x->b ^ rot64(x->c, 13);
x->b = x->c + rot64(x->d, 37);
x->c = x->d + e;
x->d = e + x->a;
}
else {
unsigned e = 0;
x->d = 1/e; /* bail */
}
#endif
return x->d;
}
void udns_jraninit(struct udns_jranctx *x, unsigned seed) {
unsigned i;
x->a = 0xf1ea5eed;
x->b = x->c = x->d = seed;
for (i = 0; i < 20; ++i)
(void)udns_jranval(x);
}

View File

@ -0,0 +1,67 @@
/* udns_misc.c
miscellaneous routines
Copyright (C) 2005 Michael Tokarev <mjt@corpit.ru>
This file is part of UDNS library, an async DNS stub resolver.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library, in file named COPYING.LGPL; if not,
write to the Free Software Foundation, Inc., 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA
*/
#include "udns.h"
int dns_findname(const struct dns_nameval *nv, const char *name) {
register const char *a, *b;
for(; nv->name; ++nv)
for(a = name, b = nv->name; ; ++a, ++b)
if (DNS_DNUC(*a) != *b) break;
else if (!*a) return nv->val;
return -1;
}
const char *_dns_format_code(char *buf, const char *prefix, int code) {
char *bp = buf;
unsigned c, n;
do *bp++ = DNS_DNUC(*prefix);
while(*++prefix);
*bp++ = '#';
if (code < 0) code = -code, *bp++ = '-';
n = 0; c = code;
do ++n;
while((c /= 10));
c = code;
bp[n--] = '\0';
do bp[n--] = c % 10 + '0';
while((c /= 10));
return buf;
}
const char *dns_strerror(int err) {
if (err >= 0) return "successeful completion";
switch(err) {
case DNS_E_TEMPFAIL: return "temporary failure in name resolution";
case DNS_E_PROTOCOL: return "protocol error";
case DNS_E_NXDOMAIN: return "domain name does not exist";
case DNS_E_NODATA: return "valid domain but no data of requested type";
case DNS_E_NOMEM: return "out of memory";
case DNS_E_BADQUERY: return "malformed query";
default: return "unknown error";
}
}
const char *dns_version(void) {
return UDNS_VERSION;
}

View File

@ -0,0 +1,169 @@
/* udns_parse.c
raw DNS packet parsing routines
Copyright (C) 2005 Michael Tokarev <mjt@corpit.ru>
This file is part of UDNS library, an async DNS stub resolver.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library, in file named COPYING.LGPL; if not,
write to the Free Software Foundation, Inc., 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA
*/
#include <string.h>
#include <assert.h>
#include "udns.h"
dnscc_t *dns_skipdn(dnscc_t *cur, dnscc_t *end) {
unsigned c;
for(;;) {
if (cur >= end)
return NULL;
c = *cur++;
if (!c)
return cur;
if (c & 192) /* jump */
return cur + 1 >= end ? NULL : cur + 1;
cur += c;
}
}
int
dns_getdn(dnscc_t *pkt, dnscc_t **cur, dnscc_t *end,
register dnsc_t *dn, unsigned dnsiz) {
unsigned c;
dnscc_t *pp = *cur; /* current packet pointer */
dnsc_t *dp = dn; /* current dn pointer */
dnsc_t *const de /* end of the DN dest */
= dn + (dnsiz < DNS_MAXDN ? dnsiz : DNS_MAXDN);
dnscc_t *jump = NULL; /* ptr after first jump if any */
unsigned loop = 100; /* jump loop counter */
for(;;) { /* loop by labels */
if (pp >= end) /* reached end of packet? */
return -1;
c = *pp++; /* length of the label */
if (!c) { /* empty label: terminate */
if (dn >= de) /* can't fit terminator */
goto noroom;
*dp++ = 0;
/* return next pos: either after the first jump or current */
*cur = jump ? jump : pp;
return dp - dn;
}
if (c & 192) { /* jump */
if (pp >= end) /* eop instead of jump pos */
return -1;
if (!jump) jump = pp + 1; /* remember first jump */
else if (!--loop) return -1; /* too many jumps */
c = ((c & ~192) << 8) | *pp; /* new pos */
if (c < DNS_HSIZE) /* don't allow jump into the header */
return -1;
pp = pkt + c;
continue;
}
if (c > DNS_MAXLABEL) /* too long label? */
return -1;
if (pp + c > end) /* label does not fit in packet? */
return -1;
if (dp + c + 1 > de) /* if enouth room for the label */
goto noroom;
*dp++ = c; /* label length */
memcpy(dp, pp, c); /* and the label itself */
dp += c;
pp += c; /* advance to the next label */
}
noroom:
return dnsiz < DNS_MAXDN ? 0 : -1;
}
void dns_rewind(struct dns_parse *p, dnscc_t *qdn) {
p->dnsp_qdn = qdn;
p->dnsp_cur = p->dnsp_ans;
p->dnsp_rrl = dns_numan(p->dnsp_pkt);
p->dnsp_ttl = 0xffffffffu;
p->dnsp_nrr = 0;
}
void
dns_initparse(struct dns_parse *p, dnscc_t *qdn,
dnscc_t *pkt, dnscc_t *cur, dnscc_t *end) {
p->dnsp_pkt = pkt;
p->dnsp_end = end;
p->dnsp_rrl = dns_numan(pkt);
p->dnsp_qdn = qdn;
assert(cur + 4 <= end);
if ((p->dnsp_qtyp = dns_get16(cur+0)) == DNS_T_ANY) p->dnsp_qtyp = 0;
if ((p->dnsp_qcls = dns_get16(cur+2)) == DNS_C_ANY) p->dnsp_qcls = 0;
p->dnsp_cur = p->dnsp_ans = cur + 4;
p->dnsp_ttl = 0xffffffffu;
p->dnsp_nrr = 0;
}
int dns_nextrr(struct dns_parse *p, struct dns_rr *rr) {
dnscc_t *cur = p->dnsp_cur;
while(p->dnsp_rrl > 0) {
--p->dnsp_rrl;
if (dns_getdn(p->dnsp_pkt, &cur, p->dnsp_end,
rr->dnsrr_dn, sizeof(rr->dnsrr_dn)) <= 0)
return -1;
if (cur + 10 > p->dnsp_end)
return -1;
rr->dnsrr_typ = dns_get16(cur);
rr->dnsrr_cls = dns_get16(cur+2);
rr->dnsrr_ttl = dns_get32(cur+4);
rr->dnsrr_dsz = dns_get16(cur+8);
rr->dnsrr_dptr = cur = cur + 10;
rr->dnsrr_dend = cur = cur + rr->dnsrr_dsz;
if (cur > p->dnsp_end)
return -1;
if (p->dnsp_qdn && !dns_dnequal(p->dnsp_qdn, rr->dnsrr_dn))
continue;
if ((!p->dnsp_qcls || p->dnsp_qcls == rr->dnsrr_cls) &&
(!p->dnsp_qtyp || p->dnsp_qtyp == rr->dnsrr_typ)) {
p->dnsp_cur = cur;
++p->dnsp_nrr;
if (p->dnsp_ttl > rr->dnsrr_ttl) p->dnsp_ttl = rr->dnsrr_ttl;
return 1;
}
if (p->dnsp_qdn && rr->dnsrr_typ == DNS_T_CNAME && !p->dnsp_nrr) {
if (dns_getdn(p->dnsp_pkt, &rr->dnsrr_dptr, p->dnsp_end,
p->dnsp_dnbuf, sizeof(p->dnsp_dnbuf)) <= 0 ||
rr->dnsrr_dptr != rr->dnsrr_dend)
return -1;
p->dnsp_qdn = p->dnsp_dnbuf;
if (p->dnsp_ttl > rr->dnsrr_ttl) p->dnsp_ttl = rr->dnsrr_ttl;
}
}
p->dnsp_cur = cur;
return 0;
}
int dns_stdrr_size(const struct dns_parse *p) {
return
dns_dntop_size(p->dnsp_qdn) +
(p->dnsp_qdn == dns_payload(p->dnsp_pkt) ? 0 :
dns_dntop_size(dns_payload(p->dnsp_pkt)));
}
void *dns_stdrr_finish(struct dns_rr_null *ret, char *cp,
const struct dns_parse *p) {
cp += dns_dntop(p->dnsp_qdn, (ret->dnsn_cname = cp), DNS_MAXNAME);
if (p->dnsp_qdn == dns_payload(p->dnsp_pkt))
ret->dnsn_qname = ret->dnsn_cname;
else
dns_dntop(dns_payload(p->dnsp_pkt), (ret->dnsn_qname = cp), DNS_MAXNAME);
ret->dnsn_ttl = p->dnsp_ttl;
return ret;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,123 @@
/* udns_rr_a.c
parse/query A/AAAA IN records
Copyright (C) 2005 Michael Tokarev <mjt@corpit.ru>
This file is part of UDNS library, an async DNS stub resolver.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library, in file named COPYING.LGPL; if not,
write to the Free Software Foundation, Inc., 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA
*/
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#ifndef WINDOWS
# include <sys/types.h>
# include <netinet/in.h>
#endif
#include "udns.h"
/* here, we use common routine to parse both IPv4 and IPv6 addresses.
*/
/* this structure should match dns_rr_a[46] */
struct dns_rr_a {
dns_rr_common(dnsa);
unsigned char *dnsa_addr;
};
static int
dns_parse_a(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end,
void **result, unsigned dsize) {
struct dns_rr_a *ret;
struct dns_parse p;
struct dns_rr rr;
int r;
/* first, validate and count number of addresses */
dns_initparse(&p, qdn, pkt, cur, end);
while((r = dns_nextrr(&p, &rr)) > 0)
if (rr.dnsrr_dsz != dsize)
return DNS_E_PROTOCOL;
if (r < 0)
return DNS_E_PROTOCOL;
else if (!p.dnsp_nrr)
return DNS_E_NODATA;
ret = malloc(sizeof(*ret) + dsize * p.dnsp_nrr + dns_stdrr_size(&p));
if (!ret)
return DNS_E_NOMEM;
ret->dnsa_nrr = p.dnsp_nrr;
ret->dnsa_addr = (unsigned char*)(ret+1);
/* copy the RRs */
for (dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr); ++r)
memcpy(ret->dnsa_addr + dsize * r, rr.dnsrr_dptr, dsize);
dns_stdrr_finish((struct dns_rr_null *)ret,
(char *)(ret->dnsa_addr + dsize * p.dnsp_nrr), &p);
*result = ret;
return 0;
}
int
dns_parse_a4(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end,
void **result) {
#ifdef AF_INET
assert(sizeof(struct in_addr) == 4);
#endif
assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_A);
return dns_parse_a(qdn, pkt, cur, end, result, 4);
}
struct dns_query *
dns_submit_a4(struct dns_ctx *ctx, const char *name, int flags,
dns_query_a4_fn *cbck, void *data) {
return
dns_submit_p(ctx, name, DNS_C_IN, DNS_T_A, flags,
dns_parse_a4, (dns_query_fn*)cbck, data);
}
struct dns_rr_a4 *
dns_resolve_a4(struct dns_ctx *ctx, const char *name, int flags) {
return (struct dns_rr_a4 *)
dns_resolve_p(ctx, name, DNS_C_IN, DNS_T_A, flags, dns_parse_a4);
}
int
dns_parse_a6(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end,
void **result) {
#ifdef AF_INET6
assert(sizeof(struct in6_addr) == 16);
#endif
assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_AAAA);
return dns_parse_a(qdn, pkt, cur, end, result, 16);
}
struct dns_query *
dns_submit_a6(struct dns_ctx *ctx, const char *name, int flags,
dns_query_a6_fn *cbck, void *data) {
return
dns_submit_p(ctx, name, DNS_C_IN, DNS_T_AAAA, flags,
dns_parse_a6, (dns_query_fn*)cbck, data);
}
struct dns_rr_a6 *
dns_resolve_a6(struct dns_ctx *ctx, const char *name, int flags) {
return (struct dns_rr_a6 *)
dns_resolve_p(ctx, name, DNS_C_IN, DNS_T_AAAA, flags, dns_parse_a6);
}

View File

@ -0,0 +1,91 @@
/* udns_rr_mx.c
parse/query MX IN records
Copyright (C) 2005 Michael Tokarev <mjt@corpit.ru>
This file is part of UDNS library, an async DNS stub resolver.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library, in file named COPYING.LGPL; if not,
write to the Free Software Foundation, Inc., 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA
*/
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "udns.h"
int
dns_parse_mx(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end,
void **result) {
struct dns_rr_mx *ret;
struct dns_parse p;
struct dns_rr rr;
int r, l;
char *sp;
dnsc_t mx[DNS_MAXDN];
assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_MX);
/* first, validate the answer and count size of the result */
l = 0;
dns_initparse(&p, qdn, pkt, cur, end);
while((r = dns_nextrr(&p, &rr)) > 0) {
cur = rr.dnsrr_dptr + 2;
r = dns_getdn(pkt, &cur, end, mx, sizeof(mx));
if (r <= 0 || cur != rr.dnsrr_dend)
return DNS_E_PROTOCOL;
l += dns_dntop_size(mx);
}
if (r < 0)
return DNS_E_PROTOCOL;
if (!p.dnsp_nrr)
return DNS_E_NODATA;
/* next, allocate and set up result */
l += dns_stdrr_size(&p);
ret = malloc(sizeof(*ret) + sizeof(struct dns_mx) * p.dnsp_nrr + l);
if (!ret)
return DNS_E_NOMEM;
ret->dnsmx_nrr = p.dnsp_nrr;
ret->dnsmx_mx = (struct dns_mx *)(ret+1);
/* and 3rd, fill in result, finally */
sp = (char*)(ret->dnsmx_mx + p.dnsp_nrr);
for (dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr); ++r) {
ret->dnsmx_mx[r].name = sp;
cur = rr.dnsrr_dptr;
ret->dnsmx_mx[r].priority = dns_get16(cur);
cur += 2;
dns_getdn(pkt, &cur, end, mx, sizeof(mx));
sp += dns_dntop(mx, sp, DNS_MAXNAME);
}
dns_stdrr_finish((struct dns_rr_null *)ret, sp, &p);
*result = ret;
return 0;
}
struct dns_query *
dns_submit_mx(struct dns_ctx *ctx, const char *name, int flags,
dns_query_mx_fn *cbck, void *data) {
return
dns_submit_p(ctx, name, DNS_C_IN, DNS_T_MX, flags,
dns_parse_mx, (dns_query_fn *)cbck, data);
}
struct dns_rr_mx *
dns_resolve_mx(struct dns_ctx *ctx, const char *name, int flags) {
return (struct dns_rr_mx *)
dns_resolve_p(ctx, name, DNS_C_IN, DNS_T_MX, flags, dns_parse_mx);
}

View File

@ -0,0 +1,128 @@
/* udns_rr_naptr.c
parse/query NAPTR IN records
Copyright (C) 2005 Michael Tokarev <mjt@corpit.ru>
Copyright (C) 2006 Mikael Magnusson <mikma@users.sourceforge.net>
This file is part of UDNS library, an async DNS stub resolver.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library, in file named COPYING.LGPL; if not,
write to the Free Software Foundation, Inc., 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA
*/
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "udns.h"
/* Get a single string for NAPTR record, pretty much like a DN label.
* String length is in first byte in *cur, so it can't be >255.
*/
static int dns_getstr(dnscc_t **cur, dnscc_t *ep, char *buf)
{
unsigned l;
dnscc_t *cp = *cur;
l = *cp++;
if (cp + l > ep)
return DNS_E_PROTOCOL;
if (buf) {
memcpy(buf, cp, l);
buf[l] = '\0';
}
cp += l;
*cur = cp;
return l + 1;
}
int
dns_parse_naptr(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end,
void **result) {
struct dns_rr_naptr *ret;
struct dns_parse p;
struct dns_rr rr;
int r, l;
char *sp;
dnsc_t dn[DNS_MAXDN];
assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_NAPTR);
/* first, validate the answer and count size of the result */
l = 0;
dns_initparse(&p, qdn, pkt, cur, end);
while((r = dns_nextrr(&p, &rr)) > 0) {
int i;
dnscc_t *ep = rr.dnsrr_dend;
/* first 4 bytes: order & preference */
cur = rr.dnsrr_dptr + 4;
/* flags, services and regexp */
for (i = 0; i < 3; i++) {
r = dns_getstr(&cur, ep, NULL);
if (r < 0)
return r;
l += r;
}
/* replacement */
r = dns_getdn(pkt, &cur, end, dn, sizeof(dn));
if (r <= 0 || cur != rr.dnsrr_dend)
return DNS_E_PROTOCOL;
l += dns_dntop_size(dn);
}
if (r < 0)
return DNS_E_PROTOCOL;
if (!p.dnsp_nrr)
return DNS_E_NODATA;
/* next, allocate and set up result */
l += dns_stdrr_size(&p);
ret = malloc(sizeof(*ret) + sizeof(struct dns_naptr) * p.dnsp_nrr + l);
if (!ret)
return DNS_E_NOMEM;
ret->dnsnaptr_nrr = p.dnsp_nrr;
ret->dnsnaptr_naptr = (struct dns_naptr *)(ret+1);
/* and 3rd, fill in result, finally */
sp = (char*)(&ret->dnsnaptr_naptr[p.dnsp_nrr]);
for (dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr); ++r) {
cur = rr.dnsrr_dptr;
ret->dnsnaptr_naptr[r].order = dns_get16(cur); cur += 2;
ret->dnsnaptr_naptr[r].preference = dns_get16(cur); cur += 2;
sp += dns_getstr(&cur, end, (ret->dnsnaptr_naptr[r].flags = sp));
sp += dns_getstr(&cur, end, (ret->dnsnaptr_naptr[r].service = sp));
sp += dns_getstr(&cur, end, (ret->dnsnaptr_naptr[r].regexp = sp));
dns_getdn(pkt, &cur, end, dn, sizeof(dn));
sp += dns_dntop(dn, (ret->dnsnaptr_naptr[r].replacement = sp), DNS_MAXNAME);
}
dns_stdrr_finish((struct dns_rr_null *)ret, sp, &p);
*result = ret;
return 0;
}
struct dns_query *
dns_submit_naptr(struct dns_ctx *ctx, const char *name, int flags,
dns_query_naptr_fn *cbck, void *data) {
return
dns_submit_p(ctx, name, DNS_C_IN, DNS_T_NAPTR, flags,
dns_parse_naptr, (dns_query_fn *)cbck, data);
}
struct dns_rr_naptr *
dns_resolve_naptr(struct dns_ctx *ctx, const char *name, int flags) {
return (struct dns_rr_naptr *)
dns_resolve_p(ctx, name, DNS_C_IN, DNS_T_NAPTR, flags, dns_parse_naptr);
}

View File

@ -0,0 +1,109 @@
/* udns_rr_ptr.c
parse/query PTR records
Copyright (C) 2005 Michael Tokarev <mjt@corpit.ru>
This file is part of UDNS library, an async DNS stub resolver.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library, in file named COPYING.LGPL; if not,
write to the Free Software Foundation, Inc., 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdlib.h>
#include <assert.h>
#include "udns.h"
int
dns_parse_ptr(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end,
void **result) {
struct dns_rr_ptr *ret;
struct dns_parse p;
struct dns_rr rr;
int r, l, c;
char *sp;
dnsc_t ptr[DNS_MAXDN];
assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_PTR);
/* first, validate the answer and count size of the result */
l = c = 0;
dns_initparse(&p, qdn, pkt, cur, end);
while((r = dns_nextrr(&p, &rr)) > 0) {
cur = rr.dnsrr_dptr;
r = dns_getdn(pkt, &cur, end, ptr, sizeof(ptr));
if (r <= 0 || cur != rr.dnsrr_dend)
return DNS_E_PROTOCOL;
l += dns_dntop_size(ptr);
++c;
}
if (r < 0)
return DNS_E_PROTOCOL;
if (!c)
return DNS_E_NODATA;
/* next, allocate and set up result */
ret = malloc(sizeof(*ret) + sizeof(char **) * c + l + dns_stdrr_size(&p));
if (!ret)
return DNS_E_NOMEM;
ret->dnsptr_nrr = c;
ret->dnsptr_ptr = (char **)(ret+1);
/* and 3rd, fill in result, finally */
sp = (char*)(ret->dnsptr_ptr + c);
c = 0;
dns_rewind(&p, qdn);
while((r = dns_nextrr(&p, &rr)) > 0) {
ret->dnsptr_ptr[c] = sp;
cur = rr.dnsrr_dptr;
dns_getdn(pkt, &cur, end, ptr, sizeof(ptr));
sp += dns_dntop(ptr, sp, DNS_MAXNAME);
++c;
}
dns_stdrr_finish((struct dns_rr_null *)ret, sp, &p);
*result = ret;
return 0;
}
struct dns_query *
dns_submit_a4ptr(struct dns_ctx *ctx, const struct in_addr *addr,
dns_query_ptr_fn *cbck, void *data) {
dnsc_t dn[DNS_A4RSIZE];
dns_a4todn(addr, 0, dn, sizeof(dn));
return
dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_PTR, DNS_NOSRCH,
dns_parse_ptr, (dns_query_fn *)cbck, data);
}
struct dns_rr_ptr *
dns_resolve_a4ptr(struct dns_ctx *ctx, const struct in_addr *addr) {
return (struct dns_rr_ptr *)
dns_resolve(ctx, dns_submit_a4ptr(ctx, addr, NULL, NULL));
}
struct dns_query *
dns_submit_a6ptr(struct dns_ctx *ctx, const struct in6_addr *addr,
dns_query_ptr_fn *cbck, void *data) {
dnsc_t dn[DNS_A6RSIZE];
dns_a6todn(addr, 0, dn, sizeof(dn));
return
dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_PTR, DNS_NOSRCH,
dns_parse_ptr, (dns_query_fn *)cbck, data);
}
struct dns_rr_ptr *
dns_resolve_a6ptr(struct dns_ctx *ctx, const struct in6_addr *addr) {
return (struct dns_rr_ptr *)
dns_resolve(ctx, dns_submit_a6ptr(ctx, addr, NULL, NULL));
}

View File

@ -0,0 +1,155 @@
/* udns_rr_srv.c
parse/query SRV IN (rfc2782) records
Copyright (C) 2005 Michael Tokarev <mjt@corpit.ru>
This file is part of UDNS library, an async DNS stub resolver.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library, in file named COPYING.LGPL; if not,
write to the Free Software Foundation, Inc., 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA
Copyright 2005 Thadeu Lima de Souza Cascardo <cascardo@minaslivre.org>
2005-09-11:
Changed MX parser file into a SRV parser file
*/
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "udns.h"
int
dns_parse_srv(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end,
void **result) {
struct dns_rr_srv *ret;
struct dns_parse p;
struct dns_rr rr;
int r, l;
char *sp;
dnsc_t srv[DNS_MAXDN];
assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_SRV);
/* first, validate the answer and count size of the result */
l = 0;
dns_initparse(&p, qdn, pkt, cur, end);
while((r = dns_nextrr(&p, &rr)) > 0) {
cur = rr.dnsrr_dptr + 6;
r = dns_getdn(pkt, &cur, end, srv, sizeof(srv));
if (r <= 0 || cur != rr.dnsrr_dend)
return DNS_E_PROTOCOL;
l += dns_dntop_size(srv);
}
if (r < 0)
return DNS_E_PROTOCOL;
if (!p.dnsp_nrr)
return DNS_E_NODATA;
/* next, allocate and set up result */
l += dns_stdrr_size(&p);
ret = malloc(sizeof(*ret) + sizeof(struct dns_srv) * p.dnsp_nrr + l);
if (!ret)
return DNS_E_NOMEM;
ret->dnssrv_nrr = p.dnsp_nrr;
ret->dnssrv_srv = (struct dns_srv *)(ret+1);
/* and 3rd, fill in result, finally */
sp = (char*)(ret->dnssrv_srv + p.dnsp_nrr);
for (dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr); ++r) {
ret->dnssrv_srv[r].name = sp;
cur = rr.dnsrr_dptr;
ret->dnssrv_srv[r].priority = dns_get16(cur);
ret->dnssrv_srv[r].weight = dns_get16(cur+2);
ret->dnssrv_srv[r].port = dns_get16(cur+4);
cur += 6;
dns_getdn(pkt, &cur, end, srv, sizeof(srv));
sp += dns_dntop(srv, sp, DNS_MAXNAME);
}
dns_stdrr_finish((struct dns_rr_null *)ret, sp, &p);
*result = ret;
return 0;
}
/* Add a single service or proto name prepending an undescore (_),
* according to rfc2782 rules.
* Return 0 or the label length.
* Routing assumes dn holds enouth space for a single DN label. */
static int add_sname(dnsc_t *dn, const char *sn) {
int l = dns_ptodn(sn, 0, dn + 1, DNS_MAXLABEL-1, NULL);
if (l <= 1 || l - 2 != dn[1])
/* Should we really check if sn is exactly one label? Do we care? */
return 0;
dn[0] = l - 1;
dn[1] = '_';
return l;
}
/* Construct a domain name for SRV query from the given name, service and proto.
* The code allows any combinations of srv and proto (both are non-NULL,
* both NULL, or either one is non-NULL). Whenever it makes any sense or not
* is left as an exercise to programmer.
* Return negative value on error (malformed query) or addition query flag(s).
*/
static int
build_srv_dn(dnsc_t *dn, const char *name, const char *srv, const char *proto)
{
int p = 0, l, isabs;
if (srv) {
l = add_sname(dn + p, srv);
if (!l)
return -1;
p += l;
}
if (proto) {
l = add_sname(dn + p, proto);
if (!l)
return -1;
p += l;
}
l = dns_ptodn(name, 0, dn + p, DNS_MAXDN - p, &isabs);
if (l < 0)
return -1;
return isabs ? DNS_NOSRCH : 0;
}
struct dns_query *
dns_submit_srv(struct dns_ctx *ctx,
const char *name, const char *srv, const char *proto,
int flags, dns_query_srv_fn *cbck, void *data) {
dnsc_t dn[DNS_MAXDN];
int r = build_srv_dn(dn, name, srv, proto);
if (r < 0) {
dns_setstatus (ctx, DNS_E_BADQUERY);
return NULL;
}
return
dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_SRV, flags | r,
dns_parse_srv, (dns_query_fn *)cbck, data);
}
struct dns_rr_srv *
dns_resolve_srv(struct dns_ctx *ctx,
const char *name, const char *srv, const char *proto, int flags)
{
dnsc_t dn[DNS_MAXDN];
int r = build_srv_dn(dn, name, srv, proto);
if (r < 0) {
dns_setstatus(ctx, DNS_E_BADQUERY);
return NULL;
}
return (struct dns_rr_srv *)
dns_resolve_dn(ctx, dn, DNS_C_IN, DNS_T_SRV, flags | r, dns_parse_srv);
}

View File

@ -0,0 +1,98 @@
/* udns_rr_txt.c
parse/query TXT records
Copyright (C) 2005 Michael Tokarev <mjt@corpit.ru>
This file is part of UDNS library, an async DNS stub resolver.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library, in file named COPYING.LGPL; if not,
write to the Free Software Foundation, Inc., 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA
*/
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "udns.h"
int
dns_parse_txt(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end,
void **result) {
struct dns_rr_txt *ret;
struct dns_parse p;
struct dns_rr rr;
int r, l;
dnsc_t *sp;
dnscc_t *cp, *ep;
assert(dns_get16(cur+0) == DNS_T_TXT);
/* first, validate the answer and count size of the result */
l = 0;
dns_initparse(&p, qdn, pkt, cur, end);
while((r = dns_nextrr(&p, &rr)) > 0) {
cp = rr.dnsrr_dptr; ep = rr.dnsrr_dend;
while(cp < ep) {
r = *cp++;
if (cp + r > ep)
return DNS_E_PROTOCOL;
l += r;
cp += r;
}
}
if (r < 0)
return DNS_E_PROTOCOL;
if (!p.dnsp_nrr)
return DNS_E_NODATA;
/* next, allocate and set up result */
l += (sizeof(struct dns_txt) + 1) * p.dnsp_nrr + dns_stdrr_size(&p);
ret = malloc(sizeof(*ret) + l);
if (!ret)
return DNS_E_NOMEM;
ret->dnstxt_nrr = p.dnsp_nrr;
ret->dnstxt_txt = (struct dns_txt *)(ret+1);
/* and 3rd, fill in result, finally */
sp = (dnsc_t*)(ret->dnstxt_txt + p.dnsp_nrr);
for(dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr) > 0; ++r) {
ret->dnstxt_txt[r].txt = sp;
cp = rr.dnsrr_dptr; ep = rr.dnsrr_dend;
while(cp < ep) {
l = *cp++;
memcpy(sp, cp, l);
sp += l;
cp += l;
}
ret->dnstxt_txt[r].len = sp - ret->dnstxt_txt[r].txt;
*sp++ = '\0';
}
dns_stdrr_finish((struct dns_rr_null *)ret, (char*)sp, &p);
*result = ret;
return 0;
}
struct dns_query *
dns_submit_txt(struct dns_ctx *ctx, const char *name, int qcls, int flags,
dns_query_txt_fn *cbck, void *data) {
return
dns_submit_p(ctx, name, qcls, DNS_T_TXT, flags,
dns_parse_txt, (dns_query_fn *)cbck, data);
}
struct dns_rr_txt *
dns_resolve_txt(struct dns_ctx *ctx, const char *name, int qcls, int flags) {
return (struct dns_rr_txt *)
dns_resolve_p(ctx, name, qcls, DNS_T_TXT, flags, dns_parse_txt);
}

View File

@ -0,0 +1,233 @@
/*
Copyright (c) 2008-2012, Troy D. Hanson http://uthash.sourceforge.net
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* a dynamic array implementation using macros
* see http://uthash.sourceforge.net/utarray
*/
#ifndef UTARRAY_H
#define UTARRAY_H
#define UTARRAY_VERSION 1.9.6
#ifdef __GNUC__
#define _UNUSED_ __attribute__ ((__unused__))
#else
#define _UNUSED_
#endif
#include <stddef.h> /* size_t */
#include <string.h> /* memset, etc */
#include <stdlib.h> /* exit */
#define oom() exit(-1)
typedef void (ctor_f)(void *dst, const void *src);
typedef void (dtor_f)(void *elt);
typedef void (init_f)(void *elt);
typedef struct {
size_t sz;
init_f *init;
ctor_f *copy;
dtor_f *dtor;
} UT_icd;
typedef struct {
unsigned i,n;/* i: index of next available slot, n: num slots */
UT_icd icd; /* initializer, copy and destructor functions */
char *d; /* n slots of size icd->sz*/
} UT_array;
#define utarray_init(a,_icd) do { \
memset(a,0,sizeof(UT_array)); \
(a)->icd=*_icd; \
} while(0)
#define utarray_done(a) do { \
if ((a)->n) { \
if ((a)->icd.dtor) { \
size_t _ut_i; \
for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \
(a)->icd.dtor(utarray_eltptr(a,_ut_i)); \
} \
} \
free((a)->d); \
} \
(a)->n=0; \
} while(0)
#define utarray_new(a,_icd) do { \
a=(UT_array*)malloc(sizeof(UT_array)); \
utarray_init(a,_icd); \
} while(0)
#define utarray_free(a) do { \
utarray_done(a); \
free(a); \
} while(0)
#define utarray_reserve(a,by) do { \
if (((a)->i+by) > ((a)->n)) { \
while(((a)->i+by) > ((a)->n)) { (a)->n = ((a)->n ? (2*(a)->n) : 8); } \
if ( ((a)->d=(char*)realloc((a)->d, (a)->n*(a)->icd.sz)) == NULL) oom(); \
} \
} while(0)
#define utarray_push_back(a,p) do { \
utarray_reserve(a,1); \
if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,(a)->i++), p); } \
else { memcpy(_utarray_eltptr(a,(a)->i++), p, (a)->icd.sz); }; \
} while(0)
#define utarray_pop_back(a) do { \
if ((a)->icd.dtor) { (a)->icd.dtor( _utarray_eltptr(a,--((a)->i))); } \
else { (a)->i--; } \
} while(0)
#define utarray_extend_back(a) do { \
utarray_reserve(a,1); \
if ((a)->icd.init) { (a)->icd.init(_utarray_eltptr(a,(a)->i)); } \
else { memset(_utarray_eltptr(a,(a)->i),0,(a)->icd.sz); } \
(a)->i++; \
} while(0)
#define utarray_len(a) ((a)->i)
#define utarray_eltptr(a,j) (((j) < (a)->i) ? _utarray_eltptr(a,j) : NULL)
#define _utarray_eltptr(a,j) ((char*)((a)->d + ((a)->icd.sz*(j) )))
#define utarray_insert(a,p,j) do { \
utarray_reserve(a,1); \
if (j > (a)->i) break; \
if ((j) < (a)->i) { \
memmove( _utarray_eltptr(a,(j)+1), _utarray_eltptr(a,j), \
((a)->i - (j))*((a)->icd.sz)); \
} \
if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,j), p); } \
else { memcpy(_utarray_eltptr(a,j), p, (a)->icd.sz); }; \
(a)->i++; \
} while(0)
#define utarray_inserta(a,w,j) do { \
if (utarray_len(w) == 0) break; \
if (j > (a)->i) break; \
utarray_reserve(a,utarray_len(w)); \
if ((j) < (a)->i) { \
memmove(_utarray_eltptr(a,(j)+utarray_len(w)), \
_utarray_eltptr(a,j), \
((a)->i - (j))*((a)->icd.sz)); \
} \
if ((a)->icd.copy) { \
size_t _ut_i; \
for(_ut_i=0;_ut_i<(w)->i;_ut_i++) { \
(a)->icd.copy(_utarray_eltptr(a,j+_ut_i), _utarray_eltptr(w,_ut_i)); \
} \
} else { \
memcpy(_utarray_eltptr(a,j), _utarray_eltptr(w,0), \
utarray_len(w)*((a)->icd.sz)); \
} \
(a)->i += utarray_len(w); \
} while(0)
#define utarray_resize(dst,num) do { \
size_t _ut_i; \
if (dst->i > (size_t)(num)) { \
if ((dst)->icd.dtor) { \
for(_ut_i=num; _ut_i < dst->i; _ut_i++) { \
(dst)->icd.dtor(utarray_eltptr(dst,_ut_i)); \
} \
} \
} else if (dst->i < (size_t)(num)) { \
utarray_reserve(dst,num-dst->i); \
if ((dst)->icd.init) { \
for(_ut_i=dst->i; _ut_i < num; _ut_i++) { \
(dst)->icd.init(utarray_eltptr(dst,_ut_i)); \
} \
} else { \
memset(_utarray_eltptr(dst,dst->i),0,(dst)->icd.sz*(num-dst->i)); \
} \
} \
dst->i = num; \
} while(0)
#define utarray_concat(dst,src) do { \
utarray_inserta((dst),(src),utarray_len(dst)); \
} while(0)
#define utarray_erase(a,pos,len) do { \
if ((a)->icd.dtor) { \
size_t _ut_i; \
for(_ut_i=0; _ut_i < len; _ut_i++) { \
(a)->icd.dtor(utarray_eltptr((a),pos+_ut_i)); \
} \
} \
if ((a)->i > (pos+len)) { \
memmove( _utarray_eltptr((a),pos), _utarray_eltptr((a),pos+len), \
(((a)->i)-(pos+len))*((a)->icd.sz)); \
} \
(a)->i -= (len); \
} while(0)
#define utarray_renew(a,u) do { \
if (a) utarray_clear(a); \
else utarray_new((a),(u)); \
} while(0)
#define utarray_clear(a) do { \
if ((a)->i > 0) { \
if ((a)->icd.dtor) { \
size_t _ut_i; \
for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \
(a)->icd.dtor(utarray_eltptr(a,_ut_i)); \
} \
} \
(a)->i = 0; \
} \
} while(0)
#define utarray_sort(a,cmp) do { \
qsort((a)->d, (a)->i, (a)->icd.sz, cmp); \
} while(0)
#define utarray_find(a,v,cmp) bsearch((v),(a)->d,(a)->i,(a)->icd.sz,cmp)
#define utarray_front(a) (((a)->i) ? (_utarray_eltptr(a,0)) : NULL)
#define utarray_next(a,e) (((e)==NULL) ? utarray_front(a) : ((((a)->i) > (utarray_eltidx(a,e)+1)) ? _utarray_eltptr(a,utarray_eltidx(a,e)+1) : NULL))
#define utarray_prev(a,e) (((e)==NULL) ? utarray_back(a) : ((utarray_eltidx(a,e) > 0) ? _utarray_eltptr(a,utarray_eltidx(a,e)-1) : NULL))
#define utarray_back(a) (((a)->i) ? (_utarray_eltptr(a,(a)->i-1)) : NULL)
#define utarray_eltidx(a,e) (((char*)(e) >= (char*)((a)->d)) ? (((char*)(e) - (char*)((a)->d))/(a)->icd.sz) : -1)
/* last we pre-define a few icd for common utarrays of ints and strings */
static void utarray_str_cpy(void *dst, const void *src) {
char **_src = (char**)src, **_dst = (char**)dst;
*_dst = (*_src == NULL) ? NULL : strdup(*_src);
}
static void utarray_str_dtor(void *elt) {
char **eltc = (char**)elt;
if (*eltc) free(*eltc);
}
static const UT_icd ut_str_icd _UNUSED_ = {sizeof(char*),NULL,utarray_str_cpy,utarray_str_dtor};
static const UT_icd ut_int_icd _UNUSED_ = {sizeof(int),NULL,NULL,NULL};
static const UT_icd ut_ptr_icd _UNUSED_ = {sizeof(void*),NULL,NULL,NULL};
#endif /* UTARRAY_H */

Some files were not shown because too many files have changed in this diff Show More