Saturday, September 7, 2013
Wednesday, June 26, 2013
Simple Credit Card Validator
Validating credit cards numbers is a fairly simple job, and there are no short supply of algorithms out there you can easily download to do it. That being said, it is a perfect algorithm to write in Haxe for the following reasons:
(Note that the ISBN10 algorithm holds some of these same properties.)
- Speed isn't super critical
- It doesn't involve the system in any way
- You can use the same code across PHP, JS, Java, C++, C#, Actionscript etc.
- The same code would be useful in all contexts.
Algorithm
The algorithm realized here is the Luhn algorithm. You can do the formal proofs with some discrete math, or trust the Wikipedia Article that it is useful because:- It detects any one digit error e.g. 1235 instead of 1234
- It detects any one transposition e.g. 1324 instead of 1234
(Note that the ISBN10 algorithm holds some of these same properties.)
The Code
/**
A simple credit card validator that just runs the luhn algorithm against the
given number. It doesn't even check to see if the string is long enough!
**/
class Validator
{
/**
Call this function with a string of digits that represent a credit card
number.
**/
public static function validate(cardNumber : String) : Bool
{
return (luhn(cardNumber) == 0);
}
private static function luhn(numericString : String) : Int
{
var output = stringToDigitArray(numericString);
var sum : Int = 0;
var isEven : Bool = true;
for( i in output.iterator())
{
if(isEven)
{
sum += sumOfDigits(i * 2);
}
else
{
sum += i;
}
isEven = !isEven; // Invert sign for next iteration
}
return sum % 10;
}
private static function stringToDigitArray(numericString : String) : Array<Int>
{
var output = new Array<Int>();
for( i in 0...numericString.length)
{
output.push(Std.parseInt(numericString.charAt(i)));
}
return output;
}
private static function sumOfDigits(number : Int) : Int
{
var sum : Int = 0;
while(number != 0)
{
sum += number % Std.int(10);
number = Std.int(number / Std.int(10));
}
sum += number;
return sum;
}
/* Examples */
public static function main() : Void
{
// False
trace(Validator.validate("1111111111111111"));
// All true below this point
trace(Validator.validate("4271058012370682"));
trace(Validator.validate("4052047247466840"));
trace(Validator.validate("4250112315345738"));
trace(Validator.validate("4302220036446780"));
trace(Validator.validate("4271382676044056"));
trace(Validator.validate("4027883785016825"));
trace(Validator.validate("4125875428334483"));
trace(Validator.validate("4231380166327600"));
trace(Validator.validate("4820020713741757"));
trace(Validator.validate("4013112216053363"));
}
}
Friday, June 21, 2013
HOWTO Find and Restore a Deleted SVN File/Folder
- Change directories to your SVN project.
- Run
svn log -v > all_history.txt
- Open
all_history.txt
and find the file you want (Ctrl + F is your friend!) - Make sure the line of the file you want begins with a D, this means it was deleted.
- Look up above that file for the revision number, subtract one from it.
- Run a new checkout for the version right beforehand i.e.:
svn co http://hostname/path/to/files_parent_directory@revision#_right_before
Example
We want to find DataFeedList.java
, here is the svn log:
------------------------------------------------------------------------
r24843 | joslewis | 2013-04-14 10:24:28 -0600 (Sun, 14 Apr 2013) | 1 line
Changed paths:
D /trunk/software/src/bmod/gui/widgets/DataFeedList.java
D /trunk/software/src/bmod/gui/widgets/FilterableComponentList.java
D /trunk/software/src/bmod/gui/widgets/JValidatingTextField.java
D /trunk/software/src/bmod/gui/widgets/WebButton.java
D /trunk/software/src/bmod/gui/widgets/WebMenuItem.java
M /trunk/software/src/bmod/plugin/generic/gui/ReportBugMenuItem.java
Modified ReportBugMenuItem to now work without WebButton. -~400 lines.
------------------------------------------------------------------------
We can see it was deleted (D) during revision r24843
by me on 2013-04-14 10:24:28 -0600. That means we want to actually get the version right before it was deleted, so we'll check out version 24842.
It was in the folder: /trunk/software/src/bmod/gui/widgets/
, so we'll craft a new checkout with the information to get it back:
svn co http://svn.host.com/trunk/software/src/bmod/gui/widgets@24842
Run this in another folder so you don't overwrite the existing source, and you'll find the deleted item in it!
Friday, June 7, 2013
The Haxe Programming Language
This is a language that has seemed to slip by under the radar for the most part.
It has a reasonable standard library that feels designed, much like Python's. The syntax is Go like, with identifiers following names:
Compilation is straightforward, consisting of an internal build system that resolves libraries well, and a global repository that you can pull new packages from.
One of the biggest benefits of this language is its ability to compile and run on so many platforms; it easily handles:
Haxe
, commonly pronounced "hex" or "hacks" is a language that targets many virtual machines, instead of the more common virtual machine that targets many languages.It has a reasonable standard library that feels designed, much like Python's. The syntax is Go like, with identifiers following names:
class WebFetch
{
static function main()
{
var output = haxe.Http.requestUrl("http://www.google.com/");
Sys.print(output);
}
}
When built, this program is a ~68Kb JAR file when targeting Java or a 550Kb ELF when generating C++.Compilation is straightforward, consisting of an internal build system that resolves libraries well, and a global repository that you can pull new packages from.
One of the biggest benefits of this language is its ability to compile and run on so many platforms; it easily handles:
- Android
- Flash
- Linux
- Mac
- Windows
- HTML + JS
- PHP
- Java (in Beta)
Example Uses
- You're a web-developer and have some business code that needs to target language of the day. Haxe can compile to language of the day without the need to translate.
- You want to code a cross-platform game to reach as broad of an audience as possible without re-writing lots of code.
- You want to develop a client for an API you publish so developers will actually use it without learning all of the languages it is going to be used in or maintaining the libraries.
- You want to develop an app that is future proof, but don't mind it being a little ugly.
Pitfalls
- There is no standardized UI system.
- The coding conventions are a strange to begin with.
- There are some obvious deficiencies in the standard library (no string compare)
- Code is slightly larger than it would be natively.
Conclusion
Overall, Haxe is a good language that I see myself using in the long run because it is so future-proof and well designed. Plus, the ability to do things easily cannot be underestimated. The example above would have taken over 100 lines of Java and about the same in C++, instead it took eight and thirty seconds to put together after knowing the language less than twenty-four hours.Thursday, May 30, 2013
Finding Strings in Raw Data
Occasionally, you'll find yourself wishing you could pull out human-readable strings from binary formats, like
The method I found that works relatively well is this:
JPEGs
, EXEs
or even raw disk dumps. This may be for a variety of reasons, such as:- Performing malware analysis
- Finding lost files
- Efficient searching
Algorithm
Actually extracting interesting text from a file is somewhat difficult because,- you don't know what format the file is in
- you don't know what language the text is in
- you don't know how relevant a snippet is
The method I found that works relatively well is this:
- Look for runs of printable characters in a file with length greater than 5
- Look for runs of uppercase letters, lowercase letters, or numbers.
- For each character of each run, set the value of the nth letter to n.
- Sum the run totals
- Sort the scores and return.
Outputs
The software below strips special terminal characters (like newlines and carriage returns), and prints out items in the following format:<score><tab><file><tab><string><newline>
Example:00000250 dd.img &'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz
00000176 dd.img .IEC 61966-2.1 Default RGB colour space - sRGB
00000176 dd.img .IEC 61966-2.1 Default RGB colour space - sRGB
00000138 dd.img ;CREATOR: gd-jpeg v1.0 (using IJG JPEG v62), quality = 95
00000136 dd.img ,Reference Viewing Condition in IEC61966-2.1
00000136 dd.img ,Reference Viewing Condition in IEC61966-2.1
00000099 dd.img Copyright (c) 1998 Hewlett-Packard Company
00000078 dd.img <?xpacket begin='
00000075 dd.img Hhttp://ns.adobe.com/xap/1.0/
00000075 dd.img Adobe Photoshop CS2 Macintosh
00000063 dd.img $54545,,4,44455,554,,4,,,4,55456,,,,,,/5554-,,4,24,
00000058 dd.img IEC http://www.iec.ch
00000058 dd.img IEC http://www.iec.ch
00000051 dd.img bottomOutsetlong
00000046 dd.img rightOutsetlong
It appears this unallocated space actually has files!Operation
Save the below code toreader.py
and provide it paths to the files you want it to read. There are no parameters.Source
#!/usr/bin/env python
'''
A program that extracts strings from binary files.
Usage: reader.py file [file ...]
Copyright (c) 2013, Joseph Lewis III <joseph@josephlewis.net> | <joehms22@gmail.com>
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.
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 HOLDER 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.
'''
import sys
ALPHABET_UPPER = frozenset("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
ALPHABET = frozenset("abcdefghijklmnopqrstuvwxyz,-. \t\n")
NUMBERS = frozenset("0123456789")
OTHER_CHARACTERS = frozenset("!\"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r")
PRINTABLE = frozenset('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r')
READ_BLOCK_SIZE_B = 2048
def type_of_char(c):
if c in ALPHABET_UPPER:
return 'A'
if c in ALPHABET:
return 'a'
if c in NUMBERS:
return 'n'
return 'o'
def value_of_string(string):
''' A heuristic to give an approximate value to a string. '''
# make sure the string isn't total junk
if all([c in OTHER_CHARACTERS for c in string]):
return 0
types = [type_of_char(c) for c in string]
runs = [0 for c in string]
# higher runs are better
for i in range(1, len(types)):
if types[i] == 'o':
runs[i] = 0
elif types[i] == types[i - 1]:
runs[i] = runs[i - 1] + 1
else:
runs[i] = 0
return sum(runs)
def process_string(string, extracts):
if len(string) < 5:
return
score = value_of_string(string)
if score > 0:
extracts.append((score, path, string))
def extract_strings(path, extracts):
'''Extracts "interesting" strings from a file'''
mystr = []
with open(path, 'rb') as fd:
chars = fd.read(READ_BLOCK_SIZE_B)
while chars != "":
for char in chars:
if char in "\r\n":
mystr.append(" ")
elif char in PRINTABLE:
mystr.append(char)
elif len(mystr) == 0:
continue
else:
process_string("".join(mystr), extracts)
mystr = []
chars = fd.read(READ_BLOCK_SIZE_B)
process_string("".join(mystr), extracts)
if __name__ == "__main__":
if len(sys.argv) == 1:
print("Usage: {} file [file ...]".format(sys.argv[0]))
exit(1)
# a list of tuples (score, document, string)
extracts = []
for path in sys.argv[1:]:
extract_strings(path, extracts)
extracts = sorted(extracts, reverse=True)
for score, path, string in extracts:
print("{:08.0f}\t{}\t{}".format(score, path, string))
Friday, May 24, 2013
Friday Operating System News
Operating Systems
NixOS is a neat little OS that is built around a single config file. The file can be moved to a different computer, and it'll set itself up as a clone, or as you're more likely to use it, as a single file that does version control on your system (so next time you upgrade, you can roll it back right away!)Jolla has released it's first phones running Sailfish OS.
Wednesday, May 15, 2013
Finding Deleted JPEGs in Disk Dumps
If you've ever accidentally deleted items from your digital camera, or want to find them from a drive that has been lost, this script could be for you!
It works by looking through a disk image for a sequence of bytes that signify the beginning and end of a JPEG image.
Then compile it:
It works by looking through a disk image for a sequence of bytes that signify the beginning and end of a JPEG image.
Running It
First save the below script as ImageCarver.javaThen compile it:
javac ImageCarver.java
Afterwards, run it with the path to the disk image you want it to search:java ImageCarver disk.img
It will report images it found, as well as the locations in the disk that it found them:1.jpg 0xe3fa 0x10272
2.jpg 0xe000 0x20865
3.jpg 0x1000 0x2557e
4.jpg 0x41000 0x4437d
5.jpg 0x26000 0x654d4
6.jpg 0x25000 0x96816
7.jpg 0x86000 0x9eca0
8.jpg 0x6d000 0xdc614
9.jpg 0xe2000 0xe7ac9
10.jpg 0xa4000 0xeb28a
11.jpg 0xed000 0xfc9a9
12.jpg 0x136000 0x13b08e
13.jpg 0x1b5144 0x1b629e
14.jpg 0x1b5000 0x1b794a
15.jpg 0x1b67f0 0x1bea75
16.jpg 0x1c9000 0x1cfada
17.jpg 0x22d000 0x237824
18.jpg 0x281000 0x288928
The Script
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.LinkedList;
/**
* Carves images from a file given from the command line.
*
* @author Joseph Lewis <joehms22@gmail.com>
*
*/
public class ImageCarver
{
byte[] STARTING_BYTES = new byte[]{(byte)0xFF, (byte)0xD8,(byte)0xFF};
byte[] ENDING_BYTES = new byte[]{(byte)0xFF,(byte)0xD9};
int imgno = 0;
/**
* Carves images out of a file.
* @param sbc - the byte channel to read from.
* @throws IOException - on a file error
*/
public ImageCarver(SeekableByteChannel sbc) throws IOException
{
// find all potential starting and ending points in the file.
LinkedList<Long> startingPoints = ringMatch(sbc, STARTING_BYTES);
LinkedList<Long> endingPoints = ringMatch(sbc, ENDING_BYTES);
// keep writing out we're out of images
while(startingPoints.size() > 0)
{
carveImage(sbc, startingPoints, endingPoints);
}
}
/**
* Carves an image out of the file.
* @param sbc - the channel to read from
* @param starts - the start positions of the image
* @param ends - the end positions of the image
* @throws IOException - on error
*/
public void carveImage(SeekableByteChannel sbc, LinkedList<Long> starts, LinkedList<Long> ends) throws IOException
{
// clear off no longer useful end points
while(starts.peek() > ends.peek())
{
ends.pop();
}
// If ends is empty, add the literal file end.
if(ends.size() == 0)
{
ends.push(sbc.size());
}
// get our starting position.
long sp = starts.pop();
// Check to see if there's an image inside of us.
if(starts.size() > 0 && starts.peek() < ends.peek())
{
carveImage(sbc, starts, ends);
}
// get our closest end point.
long endloc = ends.pop();
imgno++;
createImage(imgno + ".jpg", sbc, sp, endloc);
}
/**
* Extracts an image from the SeekableByteChannel.
* @param name - the image name
* @param src - the channel to read from
* @param begin - the offset to the beginning of the image
* @param end - the offset to the end of the image
* @throws IOException - on error
*/
public void createImage(String name, SeekableByteChannel src, long begin, long end) throws IOException
{
// alert the user about the image we're outputting
System.out.println(String.format("%s 0x%x 0x%x", name, begin, end));
// write out the given slice of bytes
try(OutputStream os = Files.newOutputStream(Paths.get(name), StandardOpenOption.CREATE))
{
src.position(begin);
ByteBuffer b = ByteBuffer.allocate((int) (end - begin));
src.read(b);
os.write(b.array());
}
}
/**
* Searches through a channel looking for a given array of bytes to match.
*
* @param sbc - the channel to read from
* @param toMatch - the bytes to match in the stream
* @return a list of all offsets where the given bytes are found
* @throws IOException - on file read error
*/
public LinkedList<Long> ringMatch(SeekableByteChannel sbc, byte[] toMatch) throws IOException
{
// holds all found vars
LinkedList<Long> finds = new LinkedList<Long>();
for(long i = 0; i < sbc.size() - toMatch.length; i++)
{
ByteBuffer b = ByteBuffer.allocate(toMatch.length);
sbc.position(i);
sbc.read(b);
boolean matches = true;
for(int j = 0; j < toMatch.length; j++)
{
if(b.array()[j] != toMatch[j])
{
matches = false;
break;
}
}
if(matches)
{
finds.add(i);
}
}
return finds;
}
public static void main(String[] args) throws Exception
{
// Make sure we have an arg.
if(args.length != 1)
{
System.out.println("Must supply exactly one argument.");
return;
}
Path p = Paths.get(args[0]);
try (SeekableByteChannel sbc = Files.newByteChannel(p))
{
new ImageCarver(sbc);
}
catch (IOException x)
{
System.out.println("Caught exception: " + x);
}
}
}
Friday, May 10, 2013
Doing a Diff on a Specific Git Version
Sometimes you just need to look up something that changed between versions of a repository instead of reverting a full file, here two
The first shows the log of your recent commits, along with hex numbers which represent the versions:
git
commands come in handy.The first shows the log of your recent commits, along with hex numbers which represent the versions:
git log
When you find the revision you want, just type:git show af78ec673f46db23ae5537a8dbea1253e6e844f3
replacing the revision number with the one you want, and you'll get a diff from that version!
Thursday, May 2, 2013
If A Robot Makes Your Makefile
If a robot makes your Makefile, it is no better than pressing that magic green arrow at the top of an IDE and watching the project compile.
While a robot may easily understand this, it isn't any better than using g++ by hand.
Makefiles, in this programmer's humble opinion, should be clean, elegant, and easy to understand. They are the easiest way to immediately jump in to a project and figure out what is important and the way it is structured.
Even more importantly, they can point to dead ends and code that is never actually accessed by the program, keeping wasted days at bay.
LIST=CPU
ifndef RECURSE
RECURSE=recurse.mk
ifdef CONFIG
RDIR=$(dir $(CONFIG))
endif
endif
include settings.mk
include $(RDIR)$(RECURSE)
While a robot may easily understand this, it isn't any better than using g++ by hand.
Makefiles, in this programmer's humble opinion, should be clean, elegant, and easy to understand. They are the easiest way to immediately jump in to a project and figure out what is important and the way it is structured.
Even more importantly, they can point to dead ends and code that is never actually accessed by the program, keeping wasted days at bay.
Wednesday, April 24, 2013
A Simple Python Plugin Framework
This framework is somewhat different than those currently out there, most of those either:
- don't allow dynamic plugins
- require you to use nasty hooks
- require specifically formatted imports
- require plugins to all be in one place
- be dynamically loaded
- be a subclass of a master plugin class
- have any name
- be in many different folders
Code
import inspect
import os
import sys
def load_plugins(plugin_path, instance):
'''Loads a set of plugins at the given path.
Arguments:
plugin_path - the OS path to look for plugins at.
instance - classes of this instance will be returned
'''
plugins = []
plugin_dir = os.path.realpath(plugin_path)
sys.path.append(plugin_dir)
for f in os.listdir(plugin_dir):
if f.endswith(".py"):
name = f[:-3]
elif f.endswith(".pyc"):
name = f[:-4]
elif os.path.isdir(os.path.join(plugin_dir, f)):
name = f
else:
continue
try:
mod = __import__(name, globals(), locals(), [], 0)
for piece in inspect.getmembers(mod):
if isinstance(piece, instance):
plugins.append(piece)
except ImportError as e:
print(e)
pass # problem importing
return plugins
Friday, April 19, 2013
Friday Link List -- Ubuntu 13.04 Backend Updates and Good News for the Web
Real Dev News for Ubuntu 13.04
This is way better than the stuff you'll find on OMGUbuntu!gvfs
updates with MTP Support (you can now connect your Android 4 device and it just works)- LibreOffice 4.0; a huge number of bugfixes went in to this
cups
updates to now auto-detect shared printers on your network, and auto-share them with other devices including iPads/iPhones etc.- Much of the backend has been ported to Python 3 in hopes of totally removing Python 2.
- Lots of work has gone in to sandboxing applications from one another (most likely due to the phone release)
- A potential new feature (if it makes it in time) is automatic removal of old kernels, which can take up a few gigs of space over the course of six months if not cleaned manually.
- New
upstart
version allowing userspace (beta) jobs, and launch on file/folder change, hopefully meaning fewer background services and less clunky code. - Fixes for quite a few RAM hogs that persist across sessions.
Web
- jQuery 2.0 has been released it drops support for IE 6-8, it looks like old Android (2x) is going to get the chop next.
- Chrome forked WebKit to form Blink, they say they were able to drop about 2.5 million lines of code right off the bat, hopefully it'll speed up their iteration time.
- Firefox has released it's baseline compiler tl;dr version, it cuts down on complexity, and does a much better job of supporting IonMonkey being it's based on the same backend giving a huge speed boost.
- ASM.js promises a good future for porting C/C++ apps to the browser with support from Firefox, and Chrome in the works.
Labels:
Chrome,
Firefox,
Friday Link List,
Javascript,
ubuntu,
Web
Wednesday, April 17, 2013
Things I Learned From Working A 2 Year 50,000 Line Project
This is mostly to serve as a brain dump for a piece of software I've been writing for around two and a half years, it was comprised of a large database (read: ~five hundred million rows), a cross platform Java application, a large Python web-server/API, a phone app, lots of small scripts that fetched and transformed data, and globs of glue. (I wrote all of them in the two year period!)
Architecture
- It pays to fully think through your APIs first
- All web APIs should have a version in the URL e.g.
/application/api/2.0/...
- Profile early and often
- A few good caches are worth many bad ones
- Make everything a plugin that extends one class; you may think it's a big hairy mess, but it's better than multiple plugin systems.
- Use libraries wherever possible
- Ditch libraries when you only use one function from them
Debugging
- Users write awful bug reports
- You can help them write better ones by giving them a short form to fill out.
- Automatically attach debugging info to all bug reports
- Make version numbers easily available (not just in the executable's name)
- Auto-update your apps so you don't have users lagging behind
Upgrades
- Your requirements will constantly change, make sure you have an easy upgrade path in your database that won't force hacky solutions.
- Make sure your ORM supports incremental changes.
User Experience
- Start fast, even if you're not done processing yet
- Never trust an engineer to do design (including software engineers)
- If the user doesn't see it, it doesn't exist
- Make it very hard for the user to do stupid things
- Do automatic backups/saves
Sanity
- Clients like saying "I did say that, didn't I? What I meant was..."
- all features should be plugins so you can enable/disable when they do this
- SVN and Eclipse are a nightmare to get playing nice
- Java 7 is really awesome!
- auto-closing streams
- multiple catches in a try block
- string in switch statements
- JNLP sucks, but so does embedding apps in-browser; just make it easy for the user to download it, and provide auto upgrading
Labels:
API,
Database,
Development,
Engineering,
Java,
Python
Friday, April 12, 2013
Covertly Sending Messages Using DNS Queries
I won't be the first to post about this, but I read it a while back and thought it sounded fun: note, this post will be more technical in nature than most, but I hope it will be a fun read, and a fun toy.
Let's say you want to send short messages that are very covert and not persistent. You could, of course use Twitter, sending strange cryptographic tweets, but that may be too obvious for your tastes, plus they can more easily be traced back to an IP/Person if just one slip-up is made. Somehow using DNS would be an awesome way to do this because:
Standard queries are the ones that your computer normally does to change a hostname in to an IP address it can contact, to do this from a command line you'd type:
That's all well and good, but how would you actually store information using DNS? For this you'll need to generate a domain name that doesn't exist, we'll be using
Now do a
This is because we passed in the
If we go back and do the
Say you just wanted to send the message
First 8
This would give us the number 2, meaning there were 2 more sections of 8, then you read on.
Let's say you want to send short messages that are very covert and not persistent. You could, of course use Twitter, sending strange cryptographic tweets, but that may be too obvious for your tastes, plus they can more easily be traced back to an IP/Person if just one slip-up is made. Somehow using DNS would be an awesome way to do this because:
- It can be accessed by anyone
- Messages expire over known intervals
- No accounts are necessary
- There are huge amounts of data to go through and junk requests that will look just like yours if someone wanted to find you out
- Google has a very nice public DNS you can use for free at 8.8.8.8 or 8.8.4.4
Specifics
The two types of queries we'll be using here are standard and non-recursive DNS queries.Standard queries are the ones that your computer normally does to change a hostname in to an IP address it can contact, to do this from a command line you'd type:
$ dig @8.8.8.8 www.google.com
To which the DNS server responds:; <<>> DiG 9.8.1-P1 <<>> @8.8.8.8 www.google.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 5459
;; flags: qr rd ra; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;www.google.com. IN A
;; ANSWER SECTION:
www.google.com. 300 IN A 74.125.225.179
www.google.com. 300 IN A 74.125.225.180
www.google.com. 300 IN A 74.125.225.176
www.google.com. 300 IN A 74.125.225.177
www.google.com. 300 IN A 74.125.225.178
;; Query time: 53 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Fri Apr 12 10:18:31 2013
;; MSG SIZE rcvd: 112
This shows you all of the numerical IP addresses you can reach google.com at, if you copy and paste the first one in to your browser, you'll see Google's home page.That's all well and good, but how would you actually store information using DNS? For this you'll need to generate a domain name that doesn't exist, we'll be using
www.testdnsflagsetting.com
for the purposes of this article:Now do a
dig
on it:$ dig @8.8.8.8 www.testdnsflagsetting.com +norecurse
The response looks different this time, note that ANSWER: 0
and AUTHORITY: 0
, this means the request is not authoratative (8.8.8.8 doesn't know this informatoin for sure), and there was no answer.This is because we passed in the
+norecurse
flag to dig
, asking the server to just reply with what was it had stored rather than asking higher up in the chain of command:; <<>> DiG 9.8.1-P1 <<>> @8.8.8.8 www.testdnsflagsetting.com +norecurse
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 23556
;; flags: qr ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;www.testdnsflagsetting.com. IN A
;; Query time: 57 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Fri Apr 12 10:04:10 2013
;; MSG SIZE rcvd: 44
Now we'll do the same thing, but without the +norecursive
:$ dig @8.8.8.8 www.testdnsflagsetting.com
; <<>> DiG 9.8.1-P1 <<>> @8.8.8.8 www.testdnsflagsetting.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 22889
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 0
;; QUESTION SECTION:
;www.testdnsflagsetting.com. IN A
;; AUTHORITY SECTION:
com. 900 IN SOA a.gtld-servers.net. nstld.verisign-grs.com. 1365782647 1800 900 604800 86400
;; Query time: 82 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Fri Apr 12 10:04:21 2013
;; MSG SIZE rcvd: 117
Note that the AUTHORITY: 1
now is true, meaning that 8.8.8.8 checked with the server that knows about all .com
addresses to see if ours existed. There is also a new section, note the number 900
in it, that means this response is going to be saved in Google's DNS (8.8.8.8) for 900 seconds.If we go back and do the
+norecurse
version again, we now get the cached response, but with 890
, meaning there are only 890 seconds left until the cache is cleared:$ dig @8.8.8.8 www.testdnsflagsetting.com +norecurse
; <<>> DiG 9.8.1-P1 <<>> @8.8.8.8 www.testdnsflagsetting.com +norecurse
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 14832
;; flags: qr ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 0
;; QUESTION SECTION:
;www.testdnsflagsetting.com. IN A
;; AUTHORITY SECTION:
com. 890 IN SOA a.gtld-servers.net. nstld.verisign-grs.com. 1365782647 1800 900 604800 86400
;; Query time: 39 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Fri Apr 12 10:04:31 2013
;; MSG SIZE rcvd: 117
Put these together now, and say you want to send a true or a false to someone on the other side of the globe, you both just have to agree on a time to check, and a domain, then if you want to say true
you do a recursive query, and the other person will get back an authoritative record when they do a non-recursive; if you don't do the query, they will not and they'll know you sent false
Sending a Whole Message
Now say you want to send a whole message! Convert the text to ASCII, and break it down to bits, use the first byte to tell how long the message will be (up to 255 characters).Say you just wanted to send the message
Hi
, that means you'd send a 2
then Hi
, but you can't use the same domain the whole way through, you'll have to agree on a bunch of domains, or a way of randomly generating them. Here we'll just append a number to the end:Letter: 2 H i
Binary: 00000010 01001000 01101001
POsition: 111111 11112222
01234567 89012345 67890123
Your queries would look something like this:dig www.testdnsflagsetting6.com
dig www.testdnsflagsetting9.com
dig www.testdnsflagsetting12.com
dig www.testdnsflagsetting17.com
dig www.testdnsflagsetting18.com
dig www.testdnsflagsetting20.com
dig www.testdnsflagsetting23.com
You only actually dig
for the 1s, because they are represented by true
.Getting The Message Back Out
Now to get the message back out, you just need to do a dig for positions 0-7, and convert that in to the number of letters in the message, multiply that by 8 to get the number of bits you need to check, and check the domains ending with all of those numbers.First 8
dig www.testdnsflagsetting0.com
dig www.testdnsflagsetting1.com
...
dig www.testdnsflagsetting7.com
This would give us the number 2, meaning there were 2 more sections of 8, then you read on.
A program to do it!
This is a short Python script to do it, you may want to change theDOMAIN_NAME
. in case other people are using the script too.#!/usr/bin/env python3
''' A program to send messages via DNS queries.
Copyright 2013 Joseph Lewis <joehms22@gmail.com> | <joseph@josephlewis.net>
MIT License
'''
import subprocess
DNS_SERVER = '8.8.8.8'
DOMAIN_NAME = "www.testdnsflagsetting{}.com"
NORECURSE_OPT = "+norecurse"
msg = raw_input("Enter a message, or blank to receive: ")
def read_byte(byteno):
byte = "0b"
for i in range(byteno * 8, (byteno + 1) * 8):
output = subprocess.check_output(['dig','@{}'.format(DNS_SERVER), DOMAIN_NAME.format(i), NORECURSE_OPT])
if ";; AUTHORITY SECTION:" in output:
byte += '1'
else:
byte += '0'
return int(byte, 2) # converts binary to an int
def write_byte(byteno, byte):
to_write = bin(byte)[2:].zfill(8) # gets binary representation of a byte
for loc, b in enumerate(to_write):
if b == '1':
i = (byteno * 8) + loc
subprocess.check_output(['dig','@{}'.format(DNS_SERVER), DOMAIN_NAME.format(i)])
print "Wrote 1 at: {}".format(i)
if len(msg) == 0:
message = ""
for byte in range(1,read_byte(0) + 1): # first byte is length of message
message += chr(read_byte(byte))
if len(message) > 0:
print message
else:
print "[No Message]"
else:
total = len(msg)
write_byte(0, total)
for loc, char in enumerate(msg):
write_byte(loc + 1, ord(char))
print "Message written"
Labels:
Cryptography,
DNS,
HowTo,
Lookup,
Networking,
Non-Recursive
Tuesday, April 9, 2013
HOWTO Not Worry About IE6 Ever Again
While IE6 market share is dwindling, we are far from being out of the woods yet. Gecko, Trident, WebKit and Blink, are likely to never convirge on a full implementation of W3C specifications, leaving some browsers more equal than others.
Not to worry though, there is a nice little script called Modernizr, that can auto-detect features of a browser and automatically load fixes for browsers that don't have the technologies you need.
Not to worry though, there is a nice little script called Modernizr, that can auto-detect features of a browser and automatically load fixes for browsers that don't have the technologies you need.
Shortlist of Features Modernizr Can Detect
- HTML5 Canvas
- IndexedDB
- WebWorkers
- Lots of CSS3 Stuff
- Geolocation
- WebSockets
Labels:
Development,
HTML,
Javascript,
Tool,
Tool Tuesday,
Web
Monday, April 8, 2013
Coding is a Drug
Coding is highly addictive. I suspect it has to do with the combination of the brain being the bottleneck, like all good puzzles; rapid iteration allowing you to see a result slowly emerge, and the fact that once you get done, you'll have something tangable as a reward that will serve you for a long time.
It's somewhat like combining crossword puzzles, woodworking, and sketching.
It's somewhat like combining crossword puzzles, woodworking, and sketching.
Monday, April 1, 2013
HowTo Calculate an MD5 hash in Java
If you're dealing with networking, or just want to make sure your files don't get corrupted/modified between times your app runs.
It is critical to use the algorithm.reset() command before using a
It is critical to use the algorithm.reset() command before using a
MessageDigest
instance, as if there are bytes still remaining from a prior digest, it will completely ruin your output.Code
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
// calculates an MD5sum for a given bit of text
public class MD5
{
public static void main(String[] args)
{
try
{
MessageDigest algorithm = MessageDigest.getInstance("MD5");
algorithm.reset(); // clear the digest back to starting state.
// Get selected digest of the given text.
byte[] digest = algorithm.digest( "Hello, world!".getBytes() );
System.out.println(byteArrToHex(digest));
}
catch (NoSuchAlgorithmException e)
{
// no such algorithm, MD5
}
}
// converts bytes to text.
private static String byteArrToHex(byte[] bytes)
{
StringBuilder sb = new StringBuilder();
for(byte b : bytes)
sb.append(String.format("%02X", b));
return sb.toString().toLowerCase();
}
}
Other Notes
- To calculate SHA1 hashes instead, use
SHA1
as the instance type. - The
NoSuchAlgorithmException
likely won't be thrown on desktop platforms, but it is possible that certain algorithms won't be implemented in certain areas due to them being considered military grade encryption; SHA1 and MD5 shouldn't have this problem because they are just simple hashes.
Thursday, March 28, 2013
The Four Keys To Good Design
The points here are stolen from The Design of Everyday Things which you should definitely purchase from the publisher.
There are four major design aspects that all interfaces should have, many already exist in the physical world due to physical processes, but need to be replicated in the visual world for users to feel in control and comfortable.
Email is a nice example, you open an "Inbox" and read "Mail" that was "Sent" to you. Of course, you don't want to tell your users that email is mail, otherwise you end up with the dreaded "Please email me back the file, I sent you the only copy I have".
But the abstraction does remove doubt from the user's mind that the process is stable and will work, even if it doesn't work in the way that they believe or you portray.
There are four major design aspects that all interfaces should have, many already exist in the physical world due to physical processes, but need to be replicated in the visual world for users to feel in control and comfortable.
Visibility
At all times, the user should be able to quickly see what is going on and what options they have.- Change icons when a process is running
- Give timers estimating how much time is left
- Gray out options that can't be chosen
- Give context-sensitive menus for a given context one of the best I've seen here is the Adobe Flash Designer whose entire bottom toolbox changed based upon what you had selected, yet it was still predictable.
A Good Conceptual Model
This is one of the biggest issues with design, especially in complex systems that could never be made in the physical world.Email is a nice example, you open an "Inbox" and read "Mail" that was "Sent" to you. Of course, you don't want to tell your users that email is mail, otherwise you end up with the dreaded "Please email me back the file, I sent you the only copy I have".
But the abstraction does remove doubt from the user's mind that the process is stable and will work, even if it doesn't work in the way that they believe or you portray.
Good Mappings
Every action should have an immediate and visible response, if the user presses "Send", don't wait on the message screen until the message is sent to return the user to the Inbox. Show the user what happened whenever they do something so they can build relations in their mind about what that is doing in software.Feedback
It doesn't matter so much that your software does computation, it just needs to inform the user of that or keep the user occupied while it is doing it; much in the same way that Mac OSX 10.6 shows an intro video: All new operating system installs need to do routine checks and get themselves in order, but there is surely a better way than this:Monday, March 18, 2013
HOWTO Quickly Create PHP Forms with phpMyEdit
One of the biggest hassles of building a site in PHP is that there is no way to easily generate forms for your database. The same is true for most systems, CRUD mechanisms take most of the design time, even when the core logic is simple.
In PHP there is a simple program called phpMyEdit that takes the time out of creating these forms. Unfortunately, it is rather old and only works with MySQL, but for most projects that is what you need.
To Setup
- Download phpMyEdit
- Extract the contents in to the directory of your site.
- Go to:
http://example.com/phpMyEdit-5.1/phpMyEditSetup.php
replacing 5.1 with your version of phpMyEdit. - Enter your database credentials and server.
- Choose your database.
- Choose your fields.
- Create the form!
Tuesday, March 5, 2013
Hacking Respondus LockDown Browser 2.0
It has been about a year since I've visited this topic, and I've decided that it is indeed worth revisiting. But first, a few reasons that I'm doing this so nobody gets the wrong idea:
This version works better, is cleaner, and anyone can edit it when it stops working. Essentially, it just modifies Respondus' window and opens other programs with button presses.
- To prove Respondus LockDown Browser does not circumvent cheating
- To encourage anyone left using this technology to change their assessment methodology to other means, such as projects because:
- They are more effective
- It is just as easy to determine if a student cheated
- They test ability to apply instead of memorization (life is open book, what you need to know is how to apply what you know!)
- To boost blog viewership (I write to be read, and Respondus is one of my most read posts)
The Technique
I have devised a better way of "hacking" Respondus that does not involve the use of monitoring system calls, although that would be equally viable and was what I had originally intended to do.This version works better, is cleaner, and anyone can edit it when it stops working. Essentially, it just modifies Respondus' window and opens other programs with button presses.
How To Run
- Download the code, and save it to a file on your desktop called
Respondus.ahk
- Download and install Auto Hot Key
- Close all other windows on your desktop.
- Right click the
Respondus.ahk
file and choose "Run As Administrator"
LockDown.exe
.Code
Run, C:\Program Files\Respondus LockDown Browser\LockDown.exe
WinWait, Respondus LockDown Browser
WinSet, AlwaysOnTop, Off, Respondus LockDown Browser
WinSet, Enable, , Respondus LockDown Browser
Gui, Add, Button, default, &Show IE
Gui, Add, Button, default, &Hide Respondus
Gui, Add, Button, default, &Show Respondus
Gui, Add, Button, default, &Maximize All
Gui, Show,, Subversion Menu
WinSet, AlwaysOnTop, On, Subversion Menu
return
ButtonShowIE:
Run, IEXPLORE.EXE http://onehourhacks.blogspot.com
WinWait, One Hour Hacks - Windows Internet Explorer
WinMove, One Hour Hacks - Windows Internet Explorer, , (A_ScreenWidth/2), 0, (A_ScreenWidth/2), (A_ScreenHeight),,
return
ButtonHideRespondus:
WinHide, Respondus LockDown Browser
return
ButtonMaximizeAll:
WinGet, WindowList, List
Loop, %WindowList%
{
WinMaximize, % "ahk_id " . WindowList%A_Index%
}
WinHide, Respondus LockDown Browser
return
ButtonShowRespondus:
WinShow, Respondus LockDown Browser
return
ButtonLeftSideRespondus:
WinMove, Respondus LockDown Browser, , 0, 0, (A_ScreenWidth/2), (A_ScreenHeight),,
return
ButtonFullScreenRespondus:
WinMove, Respondus LockDown Browser, , 0, 0, (A_ScreenWidth), (A_ScreenHeight),,
return
GuiClose:
ExitApp
Friday, March 1, 2013
The Case Against Customizability
Three words on building customizability in to your product: don't do it. This is to the products that allow users to change colors, organize panels, choose icons, etc.
Customizability just gets in the way for super-users because they have better ways of doing it.
Get your users back to the real world where they can go create art or enjoy it.
Users will also find it harder to help one another, which means more support is needed from you!
Spend your time figuring out why users would need to customize, and eliminating that need.
Users have a hard enough time with finding functionality
Watch your technophobic family members try to attach an image to an email. While you may have no problem getting through menus to do it, they often do. Remember: there are more of them than us.Customizability just gets in the way for super-users because they have better ways of doing it.
Your Product is a Tool, Not a Piece of Art
Your goal as a designer should be to minimize the amount of time on your software because it should just work.Get your users back to the real world where they can go create art or enjoy it.
Troubleshooting/Documenting is Hard With Customizability
When you write tutorials, it is easy when everything is the same. Customizability makes it harder to follow tutorials, especially if the writer has a custom setup.Users will also find it harder to help one another, which means more support is needed from you!
It Leads to Messy Code
Just think about rearranging components on the web, you need a place to save the information, a new server interface for submitting it, security procedures over that to make sure it isn't an attack vector, another library user-side to test in every browser.Most of the Users Will Never Do It
Take BlackBoard for example, I TA a bit, and of all the students I've seen pull up blackboard, not one of them has changed the style or layout as they're allowed to; nobody wants to spend the time on a product they use twice a day.Your time is better spent on usability
iGoogle was a great example of this, instead of needing a homepage like they used to, users can now just type what they want in to the search box and get information relevant to them.Spend your time figuring out why users would need to customize, and eliminating that need.
Final Note
I'm not advocating that you throw everything in to code, no no! I fully support MVC, that is how superusers can change your environment to suit them best. If you must allow user tweaks to your software, like moving around menus, put it in a plugin so when your code gets updated, the whole software doesn't break.Thursday, February 21, 2013
HOWTO Find the Number of Pixels in a MM with Javascript
This is a simple trick to find the number of pixels in a millimeter using jQuery.
Code
function pixelsPerMM()
{
$("body").append("<div id='onebyone' style='width:1mm;height:1mm;display:hidden;'></div>");
var pixels = $("#onebyone").width();
$("#onebyone").remove();
return pixels;
}
How It Works
- Adds a new element that is 1mm x 1mm to the page.
- Requests the width (in pixels) of that element.
- Deletes the element.
Why Use It
If you have an interface that you want to be exactly the same on every device, but you can't lay out the entire thing using CSS i.e. some elements are dynamically generated using Javascript, this would be a good method.Saturday, February 16, 2013
When You Are Justified in Complaining About FOSS
LibreOffice 4.0 has recently been released, and has received a lot of unfounded criticism over its new ability to be themed using Firefox Personas. Now would be a good time to go over when it is acceptable or not to complain about FOSS, as there seems to be quite a lot of confusion about that.
Most of the time, you should not complain about FOSS like this because:
Not only does LO 4.0 work better than the 3.X branches, it is vastly superior in speed, supported formats, and code maintainability.
This whole issue will seem silly in two months when Ubuntu releases a branded LO using a theme that adds subtleties to the document editor that perfectly integrate it with the platform, but until then hopefully these guidelines will suffice for indignant posters.
Most of the time, you should not complain about FOSS like this because:
- You got it for free.
- If you don't like it, you don't have to use it.
- You are encouraged to fix the things that annoy you.
- You did not do any work.
- When the product destroys your data.
- When there is no way to contribute to the project other than forking it. (e.g. Android)
- When you have contributed code/patches that are widely desired, but the maintainers will not accept them.
- When you are paying the developers to make changes, and they are not.
Back to Personas!
All of the complaints the LO team have received over this issue are unfounded because the software is free, does not force you to update to the newest version, has a remarkably open ecosystem (1500 merges from 3.0 to 4.0), and the product still works without ever using that feature.Not only does LO 4.0 work better than the 3.X branches, it is vastly superior in speed, supported formats, and code maintainability.
This whole issue will seem silly in two months when Ubuntu releases a branded LO using a theme that adds subtleties to the document editor that perfectly integrate it with the platform, but until then hopefully these guidelines will suffice for indignant posters.
Tuesday, February 12, 2013
HOWTO Make a Quick and Dirty (Pseudo)Random Number Generator
If you ever need to build your own quick and dirty (pseudo-)random number generator it actually isn't that hard. This generator is used in various real-world systems:
- Visual Basic (to version 6)
- A few ANSI C implementations, including
glibc
- Java's Random library
a
, m
,c
's. 1Implementation
#include <stdio.h>
#include <unistd.h>
const long m = 4294967296; // 2 ^ 32
const long a = 1103515245;
const long c = 12345;
long lastX = 0;
long nrandom() {
lastX = (a * lastX + c) % m;
return lastX;
}
void seed(int num)
{
lastX = num;
}
int main()
{
int i;
seed(getpid());
for(i = 0; i < 1000; i++)
printf("%li\n", nrandom());
}
Note that this implementation only generates m pseudorandom numbers, in this case 4,294,967,296; so if you need more, you'll want to use a different algorithm.- These conditions require some discrete mathematics that are too long to go in to here; it involves coprime numbers and recurrence relations. (Please point out if I've missed some simple explaination in the comments, I'm fairly new to this!)↩
Wednesday, February 6, 2013
HOWTO Fix Flash in Chrome + Ubuntu 12.10
After a recent Chrome update, I was left without flash for this browser, which by the way handles Pandora way better than Firefox does on the same platform.
A quick fix is all that is needed:
A quick fix is all that is needed:
- Type chrome://plugins/ in to your URL bar.
- At the upper right, press "Details"
- Scroll down to the Adobe Flash Player entry, there should be two sub-entries, these are versions of Flash Chrome can use.
- Disable the entry that looks like this (should be the first one):
Name: Shockwave Flash
Location: /home/user/.config/google-chrome/PepperFlash/11.5.31.138/libpepflashplayer.so
Type: PPAPI (out-of-process)
MIME types:
MIME type Description File extensions
application/x-shockwave-flash Shockwave Flash .swf
application/futuresplash Shockwave Flash .spl - Restart the browser.
Monday, February 4, 2013
HOWTO Use Python "for in" and "for else" Loops
This is a short guide to the subtleties of Python
The Basic
If you come from a Java/C++/PHP background, you're used to two different
In Java, your for loop would look something like this:
If you wanted 0-9 inclusive, the loop would be even more simple.
In fact, you can loop over any list of tuples in a similar manner:
The Magic of the
If you've ever wanted to check if something happened within a for loop you can use an
for
loops, especially for those programmers from Java/C++ that may miss some of the best features because those languages don't have any equivalent, it covers:- The basic
for
loop - Looping over dictionaries
- Using the
for else
loop
The Basic for
loop
If you come from a Java/C++/PHP background, you're used to two different for
loops foreach
and for
. Python only has one of these, the more powerful foreach
.In Java, your for loop would look something like this:
for(int i = 0; i < 10; i += 2)
{
System.out.println(i);
}
In Python, the same loop would look like;for i in range(0, 10, 2):
print(i)
The range
function returns a list of numbers 0 to 10 incremented by 2.If you wanted 0-9 inclusive, the loop would be even more simple.
for foo in range(10):
print(foo)
In this same manner, you can replace the range()
with any list:for j in [1,1,2,3,5]:
print(j)
Looping over Dictionaries (maps)
Maps are a very handy Python builtin feature, allowing you to create arbitrary key->value pairs.{
"key":"value",
"bob":"smith",
"age":13,
"dob":(2012,01,01)
}
You can quickly loop over key->value pairs in a dictionary by using the following:for key, value in a.items():
print("{} -> {}".format(key, value))
Note that this is much faster than looping over the keys, then looking up the value for each key inside the for loop. The items
function returns a list of tuples where the first value is the key, and the second a value.In fact, you can loop over any list of tuples in a similar manner:
b = [("Perro", 1, ["Canis", "lupis"]),
("Gato", 2, ["Felis", "catus"])]
for name, id, scientific in b:
print("Name: {} ID: {} Breed: {}".format(name, id, " ".join(scientific)))
The Magic of the for else
Loop
If you've ever wanted to check if something happened within a for loop you can use an else
block after your for loop:for i in ["foo","bar"]:
if i == "baz":
break
else:
# happens if break is not called, meaning "baz" was not found
return -1
# i is set to the location of "baz"
return i
The example above tries to find the location of a specified string in a list of strings, if found it returns the index, otherwise -1. Of course there are far easier ways to do this, but you get the idea.
Wednesday, January 30, 2013
HOWTO Debug Crashes in C/C++ Applications on Ubuntu
In this howto we'll cover:
The way of debugging in Ubuntu when coding by hand is not nearly as nice as popping up an IDE, but when done right can be much faster.
If you run this program, it will die nearly right away with the error:
The -ggdb flag to the compiler instructs it to include lots of debugging information. However, you won't want this on production executables because it takes up a lot of space. This example program was 9.6K with symbols, and 6.2K without!
Common
- Compiling C/C++ code for debugging
- Allowing debugging
- Viewing errors
- Fixing common
gdb
issues
Segmentation fault (core dumped)
your computer yells before it plunges back to darkness and your friendly shell prompt reappears.The way of debugging in Ubuntu when coding by hand is not nearly as nice as popping up an IDE, but when done right can be much faster.
Our Problematic Code
For demonstration we'll be using this bit of code that is written to cause crashes.int main()
{
int* p = 0x0000007b; // The cause of many a Windows XP BSOD
int j;
for(j = 0; j < 10000000; j++)
{
p++;
*p = j;
}
return 1;
}
In order to get the best debugging results you'll have to compile your code with the -ggdb
flag, in this instance: gcc -ggdb killer.c
.If you run this program, it will die nearly right away with the error:
Segmentation fault (core dumped)
.The -ggdb flag to the compiler instructs it to include lots of debugging information. However, you won't want this on production executables because it takes up a lot of space. This example program was 9.6K with symbols, and 6.2K without!
Enabling the Dump
By default, Ubuntu 12.10 won't output crash information for programs you make yourself, to fix this you'll need to run the command:ulimit -c unlimited
This will need to be run every time you log back on to your system.Debugging
Once you have logging enabled and your program crashes a file calledcore
should appear in the directory from which you ran the program. Use the gdb
command to see what information it contains about the crash.gdb a.out core
Where a.out is the name of your program that crashed and created the core
file.Analyzing the Debug Output
GNU gdb (GDB) 7.5-ubuntu
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/joseph/Desktop/a.out...done.
[New LWP 13663]
warning: Can't read pathname for load map: Input/output error.
Core was generated by `./a.out'.
Program terminated with signal 11, Segmentation fault.
#0 0x00000000004004ed in main () at breakme.c:9
9 *p = j;
This is the output you'll get from the gdb
command, all you want is the last three lines, from the top they tell you:- What happened.
- Where the error happened (in main in file breakme.c on line 9)
- What the line was.
Common gdb
Errors
warning: exec file is newer than core file.
means the core file you're using wasn't made by your executable, you'll need to delete it and run your program again.warning: core file may not match specified executable file.
means the core file you're running against probably wasn't made by the program you specified,gdb
will probably report wrong results.
Tuesday, January 29, 2013
Extended Euclidean Algorithm in Python
The Extended Euclidean Algorithm is a simple extension to the Euclidean Algorithm that in addition to returning the greatest common denominator also gives numbers s and t such that gcd = s * a + t * b.
Specifically, you may want to find 1 = sa + tb which is very useful in cryptography.
Specifically, you may want to find 1 = sa + tb which is very useful in cryptography.
The Code
def extended_euclidean(a,b):
origa = a
origb = b
x = 0
lastx = 1
y = 1
lasty = 0
while b != 0:
q = a // b
a, b = b, a % b
full = "%d = (%d * %d + %d * %d) - %d * (%d * %d + %d * %d) " % ( b, origa, lastx, origb, lasty, q, origa, x, origb, y )
short = "%d = %d * %d + %d * %d" % ( b, origa, lastx - x * q, origb, lasty - y * q)
print("%s\t%s\t%s\t%s" % (a, b, full, short))
x, lastx = (lastx - q * x, x)
y, lasty = (lasty - q * y, y)
return (lastx, lasty)
The Output
Take the numbers 120 and 23:>>> extended_euclidean(120,23)
23 5 5 = (120 * 1 + 23 * 0) - 5 * (120 * 0 + 23 * 1) 5 = 120 * 1 + 23 * -5
5 3 3 = (120 * 0 + 23 * 1) - 4 * (120 * 1 + 23 * -5) 3 = 120 * -4 + 23 * 21
3 2 2 = (120 * 1 + 23 * -5) - 1 * (120 * -4 + 23 * 21) 2 = 120 * 5 + 23 * -26
2 1 1 = (120 * -4 + 23 * 21) - 1 * (120 * 5 + 23 * -26) 1 = 120 * -9 + 23 * 47
1 0 0 = (120 * 5 + 23 * -26) - 2 * (120 * -9 + 23 * 47) 0 = 120 * 23 + 23 * -120
(-9, 47)
Thursday, January 24, 2013
HOWTO Setup Automatic Code Hilighting for Websites (and Blogger)
This is actually the method that this blog uses! The scripts below quickly find all code on a page, auto-detect what language it is, and highlight it accordingly.
If all of your code is in
Side Note: You probably want to use your own versions of
Setting Up On Blogger
To set up on Blogger all you need to do is the following:- Open the "Template" menu on the left side of your blog's dashboard.
- Press the "Edit HTML" button below your template.
- Just copy and paste the following code right before the
</html>
tag.
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript"></script>
<script src="http://balupton.github.com/jquery-syntaxhighlighter/scripts/jquery.syntaxhighlighter.min.js" type="text/javascript"></script>
<script type="text/javascript">
$('pre').each(function() {
$(this).addClass( "highlight" );
});
$.SyntaxHighlighter.init();
</script>
On Your Website
Alternately, you can add this to your website for automatic code finding and hilighting! Just make sure to add it to the very bottom of your page, or add the third Javascript code block that runs once the page is loaded.If all of your code is in
<code>
blocks, just change the pre
on line four to code
.Side Note: You probably want to use your own versions of
jQuery
and jQuerySyntaxHilighter
so you can update them yourself.
Sunday, January 20, 2013
Extract Email Attachments With Python
If you archive your email messages, like me, you may find that you want to pull out all of the attachments for those files so your desktop search will parse them better, or so you can quickly search through them.
This is a simple script that just recurses through your
For those of you that are wondering what
This is a simple script that just recurses through your
.eml
messages in a directory and pulls out all of the base64
encoded attachments.For those of you that are wondering what
base64
is, it's an encoding that only uses sixty-four different characters to transmit information. The email system uses this to send documents around so that the protocol didn't have to be reconfigured to account for stuff that wasn't text.Code
#!/usr/bin/env python
import email.parser
import os
import sys
import base64
fileList = []
rootdir = "/path/to/.eml/messages/"
for root, subFolders, files in os.walk(rootdir):
for file in files:
fileList.append(os.path.join(root,file))
id = 0
for path in fileList:
if not path.endswith(".eml"):
continue
fp = email.parser.FeedParser()
fp.feed(open(path).read())
message = fp.close()
for message in message.walk():
fn = message.get_filename()
if fn == None:
continue
try:
with open(fn, 'wb') as out:
out.write(base64.b64decode(message.get_payload()))
except TypeError:
with open(fn, 'wb') as out:
out.write(message.get_payload())
Extensions
- This script isn't very efficient being that it uses python to decode.
- It would be nice to pull arguments from the command line using
sys.argv
Update 2013-09-04 Python 3
#!/usr/bin/env python3
import email.parser
import os
import sys
import base64
import binascii
import sys
def extract(rootdir):
fileList = []
for root, subFolders, files in os.walk(rootdir):
for file in files:
fileList.append(os.path.join(root,file))
for path in fileList:
if not path.endswith(".eml"):
continue
fp = email.parser.BytesFeedParser()
fp.feed(open(path, "rb").read())
message = fp.close()
print("Checking {}".format(path))
for message in message.walk():
fn = message.get_filename()
if fn == None:
continue
try:
try:
with open(fn, 'wb') as out:
out.write(message.get_payload(decode=True))
except (TypeError, binascii.Error):
with open(fn, 'wb') as out:
print(message.get_payload())
out.write(bytes(message.get_payload(), message.get_charset()))
except Exception:
print("Error extracting item from {}".format(path))
if __name__ == "__main__":
if len(sys.argv) == 1:
print("usage: {} path/to/.eml/files".format(sys.argv[0]))
exit(1)
extract(sys.argv[1])
Wednesday, January 16, 2013
HOWTO Redirect A Page Using Javascript
Occasionally you need to move a page on a website from one location to another, but don't have the ability to update all of the links that point to the old one or access to the server software to do the same thing.
All you need is a page with a little Javascript to solve the problem.
All you need is a page with a little Javascript to solve the problem.
Example
<html>
<head>
<title>Redirecting...</title>
<script type='text/javascript'>
window.location.replace("http://your.site.here");
</script>
</head>
<body>
<p>Redirecting...if you are not redirected click
<a href='http://your.site.here'>here</a>.</p>
</body>
</html>
Notes
- Include a link and Javascript to redirect for users that have JS turned off.
- Use
window.location.replace
instead ofwindow.location.href
so the user doesn't get stuck in a loop when they press the back button. - Use
window.location.replace
instead ofdocument.location
- Insert
if(top != self) top.location.replace("http://your.site.here");
in the top of the<script>
tag to escape from frames.
Monday, January 14, 2013
Ubuntu and the Art of Deception
I am, and have been an Ubuntu user for almost six years; over the past four, I've given up Windows entirely (skipping over Vista and 7, save for rescuing family members' PCs occasionally). However, the more and more Ubuntu grows the more I notice discrepancies between the Linux that I loved originally, and the current incarnation.
When Shuttleworth gets out and states all of the great things that came to us in 12.10 the major ones were these:
When all of this came out, webapps were touted as a great fusion of desktop and web, where in reality, they work on when the pages are already open, saving a few clicks at best, and opening your system to security flaws at worst. Why not support W3C apps? That would be a huge boon to the software overall, and a push to help Tizen. What about supporting Firefox apps? Quick to develop, quick and open to deploy.
The Amazon advertisements are a huge privacy concern, according to the EFF, rather than some magical new way to shop.
Perhaps Shuttleworth has spread himself too thin; rather than shoring up upstream apps and using them (i.e. docky could have easily been used instead of unity) he has created his own; rather than creating a unified configuration file set, we have a hack that works with each individual application.
It would be nice to see a desktop, particularly the most powerful one, contribute far more to the small projects that make it up, rather than fork them, until that happens, Ubuntu will become more and more of an impenetrable island that will eventually grind to a halt where a finite set of resources is used to do everything.
The Deception
In the most recent release, the biggest change was the addition of a web API that interfaces with the desktop. Don't get me wrong, this is great, but a very underwhelming thing to take SIX MONTHS to develop. Had I wished to do so, I could have perfected the same thing (because the back-end is already totally there with DBus) in about forty hours of work. So, if Canonical had one developer working on it, for one week, it would have been done. What happened the rest of the time?When Shuttleworth gets out and states all of the great things that came to us in 12.10 the major ones were these:
- Webapps - as discussed about 40 hours of time
- Online accounts - already integrated in Gnome, a simple port was all that was needed
- Dash Previews - essentially a few more plugins to the dash, which should be about a week of programming, especially if you use the utilities already available, like the "file" command
- Easy full disk encryption - Essentially a single change to the installer
- Ditching Unity 2D - this should have freed up a lot of developer time!
- And the hotly contested Amazon Search Results - advertisements for your PC that I'm sure Amazon would have implemented due to all the free advertising
The News
So, what did the news sites report on for six months? Theme changes, new button gradients, new backgrounds, etc. All the real work I could have done quicker, and not included the ads.When all of this came out, webapps were touted as a great fusion of desktop and web, where in reality, they work on when the pages are already open, saving a few clicks at best, and opening your system to security flaws at worst. Why not support W3C apps? That would be a huge boon to the software overall, and a push to help Tizen. What about supporting Firefox apps? Quick to develop, quick and open to deploy.
The Amazon advertisements are a huge privacy concern, according to the EFF, rather than some magical new way to shop.
The Future
So, what does this all mean? Ubuntu has reached the feature creep stage, because it's founder won't take risks; maybe he's outpaced himself with a six month development cycle instead of continuous integration, but not enough is changing other than seeming regressions that users have to fight with each subsequent "release", but a great number of users refuse to admit this, probably because they don't know enough how quickly these changes could be made to a well designed system.Perhaps Shuttleworth has spread himself too thin; rather than shoring up upstream apps and using them (i.e. docky could have easily been used instead of unity) he has created his own; rather than creating a unified configuration file set, we have a hack that works with each individual application.
It would be nice to see a desktop, particularly the most powerful one, contribute far more to the small projects that make it up, rather than fork them, until that happens, Ubuntu will become more and more of an impenetrable island that will eventually grind to a halt where a finite set of resources is used to do everything.
Wednesday, January 9, 2013
If You Think Your Users Are Stupid, You're Probably Doing it Wrong
I was recently in an office where a programmer and a designer were complaining about their users being too stupid.
The users weren't using the program they were tasked with developing "properly". If you develop an application that the users cannot figure out, you have failed to do your job, and are asking the users to do it for you.
As an application developer, it is your job to be invisible, and to make the software invisible. The user, novice or expert, should be able to get in to your system, figure out how it works immediately, do what they need to, and leave easily.
One of the most valuable things I've found out as a developer is that users rarely think in the same models as you. As programmers, we're taught to divide the world in to neat little problems and objects that flow through pipes and get transformed.
The user doesn't understand that the box that helps them compose email is a text pane wrapped in a scroll pane that has buttons that apply styles to the data inside that passes it to a renderer, and has a side process that is activated whenever a whitespace token is pressed that does a spelling check on the document. They probably don't imagine that the place to enter names that looks them up in their contacts database is a separate piece of software altogether. To the user, the object they see is a form.
This leads me to a few, simple rules for usability design that I think everyone should benefit from:
If you have a confusing area in your software, use contextual popups (when the user is editing a form) or help can help ferry the user through potentially confusing areas.
The users weren't using the program they were tasked with developing "properly". If you develop an application that the users cannot figure out, you have failed to do your job, and are asking the users to do it for you.
As an application developer, it is your job to be invisible, and to make the software invisible. The user, novice or expert, should be able to get in to your system, figure out how it works immediately, do what they need to, and leave easily.
One of the most valuable things I've found out as a developer is that users rarely think in the same models as you. As programmers, we're taught to divide the world in to neat little problems and objects that flow through pipes and get transformed.
The user doesn't understand that the box that helps them compose email is a text pane wrapped in a scroll pane that has buttons that apply styles to the data inside that passes it to a renderer, and has a side process that is activated whenever a whitespace token is pressed that does a spelling check on the document. They probably don't imagine that the place to enter names that looks them up in their contacts database is a separate piece of software altogether. To the user, the object they see is a form.
This leads me to a few, simple rules for usability design that I think everyone should benefit from:
Anticipate the User's Needs
If you are writing an email app, auto-suggest contacts to use as recipients, offer printer-friendly versions of everything (preferably that auto-format for printers).Consistency
Just because you're designing a website for My Little Pony watchers doesn't mean purple links is a good idea (forgive me if I've gotten this entirely wrong), blue links are standard, and they are what users look for.- Buttons are not links, and links are not buttons (links navigate and buttons change the current page)
- If two things look the same, they should do the same thing.
- If something is dangerous
Navigation
Or, "where the hell am I?" generally you don't need folders in folders in folders; if you do, you're doing something horribly wrong. Even desktop applications benefit from clear location awareness.If you have a confusing area in your software, use contextual popups (when the user is editing a form) or help can help ferry the user through potentially confusing areas.
Speed
This one is probably the most useful. Find out what 90% of your users are doing (it is probably either not what you intended, or they are doing what you wanted in a long and laborious way), so try doing the following:- Remove all items in a form that aren't required.
- Allow sign-in from existing accounts, like Google/Twitter/Facebook if applicable.
- Show the user the status of their most used items right away, using colors if possible.
- Use consistent icons.
- Use consistent navigation.
- When the users are working around somthing you've made, fix your software to facilitate their way (ideally you'd make whatever way you had in mind the easiest so it would be quickly discovered and used)
Sunday, January 6, 2013
End of Blogging For A Month
Over the past month I made it a goal to write a post every day. It was tough trying to find something to write about every day. Even if it seemed easy to begin with.
The hardest part was trying to write well, rather than just send links (and yes, I cheated on Fridays with a link list, but so does everyone else, I regret nothing!)
Overall, the experience has been good, I've nearly now got a hundred posts and roughly fifty page views a day, and my most popular articles have exploded while others don't, I'm not entirely sure why yet, but I'm determined to find out.
In future months, I plan to cut back on the schedule, probably to three times a week, now that I know it isn't as bad as I imagined.
What's up next? Hmm, I'm not sure yet; I may write a book about something, exercise or whatever, I'll think about it for a day.
Here is the video that originally made me decide to try something new for thirty days every thirty days:
My last endeavor was to take a picture every day for a month, and it was great!
The hardest part was trying to write well, rather than just send links (and yes, I cheated on Fridays with a link list, but so does everyone else, I regret nothing!)
Overall, the experience has been good, I've nearly now got a hundred posts and roughly fifty page views a day, and my most popular articles have exploded while others don't, I'm not entirely sure why yet, but I'm determined to find out.
In future months, I plan to cut back on the schedule, probably to three times a week, now that I know it isn't as bad as I imagined.
What's up next? Hmm, I'm not sure yet; I may write a book about something, exercise or whatever, I'll think about it for a day.
Here is the video that originally made me decide to try something new for thirty days every thirty days:
My last endeavor was to take a picture every day for a month, and it was great!
Saturday, January 5, 2013
Three New Interesting Operating Systems
AROS Icaros
Not an OS per se, but a desktop environment that allows you to run old Amiga software; it allows full emulation for Amiga software without the need for ROMs, in the latest version.Whonix
This is an interesting concept of an OS, the user runs inside of a virtual machine where all the internet traffic is automatically routed through TOR. Somehow I doubt this will come to as great of use as it should, simply because running a VM is very costly, especially through VirtualBox (which this is), maybe if it were running on Xen. The project just got released so there is still time for improvement!FreeBSD 9.1
The latest FreeBSD has been released (FreeBSD is the thing Apple based OSX on). New in this release are- Xen ethernet driver
- IPv6 improvements
- C++11 stack
- New Intel Drivers
Thursday, January 3, 2013
Visualizing REGEXs
The excellent REGEXPER generates beautiful representations of Javascript regexes you send its way.
Example (IP Address)
\d\d?\d?\.\d\d?\d?\.\d\d?\d?\.\d\d?\d?
Tuesday, January 1, 2013
ODT Text Mining in Python
The
ODT
format is slowly gaining popularity, as governments worldwide begin to either use FOSS software, or transition to formats that are open enough to still be usable in at least a hundred years.The Format
Within all.odt
files (which are just zipfiles) there is an XML file that holds all of the document's text named content.xml
. All of the characters between the XML tags are the document's text, so by cutting out all of the tags, all that remains is the text (what the script below does).The Code (Python)
#!/usr/bin/env python3
'''
Copyright 2012 Joseph Lewis <joehms22@gmail.com> | <joseph@josephlewis.net>
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 the nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 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.
This software recieves an odt file from the command line, and returns the text
from its path.
'''
import zipfile
import xml.etree.ElementTree
import sys
def extract_odt_text(fp):
myFile = zipfile.ZipFile(fp)
share = xml.etree.ElementTree.fromstring(myFile.read('content.xml'))
text_nodes = []
for elt in share.iter():
if elt.text != None:
text_nodes.append(elt.text.strip())
return " ".join(text_nodes)
if __name__ == "__main__":
if len(sys.argv) == 1:
print "usage: odt.py FILENAME [FILENAME...]"
else:
for arg in sys.argv:
try:
print(extract_odt_text(arg))
except zipfile.BadZipfile:
pass
Subscribe to:
Posts (Atom)