AMI test client for VoLTE development
This commit is contained in:
parent
2c05b79baf
commit
438e066c70
|
@ -0,0 +1 @@
|
|||
ami_test_client
|
|
@ -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.
|
|
@ -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
|
|
@ -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.
|
|
@ -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.
|
|
@ -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) {
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,298 @@
|
|||
|
||||
#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");
|
||||
ami_action(ami, event_response, NULL, "Action: PJSIPAccessNetworkInfo\nRegistration: volte_ims\nInfo: hello");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static int registered=0;
|
||||
|
||||
void event_registry (ami_event_t *event) {
|
||||
const char *status_str;
|
||||
status_str = ami_getvar(event, "Status");
|
||||
|
||||
printf("registry-status=%s\n", status_str);
|
||||
|
||||
if (!strcasecmp(status_str, "Registered")) {
|
||||
if (!registered) {
|
||||
registered = 1;
|
||||
ami_action(ami, event_response, NULL, "Action: PJSIPUnregister\nRegistration: volte_ims");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
ami_event_register(ami, event_registry, NULL, "Event: Registry");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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
|
@ -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.
|
|
@ -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
|
|
@ -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.
|
||||
|
||||
|
|
@ -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.
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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 jó, 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;
|
||||
}
|
|
@ -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, ...);
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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))
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
*/
|
||||
|
|
@ -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
|
||||
*/
|
||||
|
|
@ -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.
|
|
@ -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
|
|
@ -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/
|
|
@ -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;
|
||||
}
|
|
@ -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__
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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)
|
|
@ -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;
|
||||
}
|
|
@ -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__ */
|
|
@ -0,0 +1,6 @@
|
|||
[protocol]
|
||||
version = 5 ; próba komment
|
||||
|
||||
[user]
|
||||
name = Gipsz Jakab ; comment
|
||||
email = gip@szja.kab ; comment
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
This is an error
|
|
@ -0,0 +1 @@
|
|||
indented
|
|
@ -0,0 +1,5 @@
|
|||
[section1]
|
||||
name1=value1
|
||||
[section2
|
||||
[section3 ; comment ]
|
||||
name2=value2
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,3 @@
|
|||
[bom_section]
|
||||
bom_name=bom_value
|
||||
key“ = value“
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
[section]
|
||||
a = b
|
||||
user = parse_error
|
||||
c = d
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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__)
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
@ -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 rá. 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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
@ -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!
|
|
@ -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)
|
|
@ -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.
|
|
@ -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.
|
|
@ -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?
|
|
@ -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
|
||||
}
|
|
@ -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).
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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 */
|
|
@ -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.
|
|
@ -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
|
@ -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 */
|
|
@ -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
|
|
@ -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));
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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));
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
Loading…
Reference in New Issue